lamphua 1 неделю назад
Родитель
Сommit
52e8fe2c84

+ 4
- 8
oa-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/oa/CmcContractController.java Просмотреть файл

@@ -117,8 +117,7 @@ public class CmcContractController extends BaseController
117 117
                             yearAmountI = yearAmountI.add(contract.getAmount());
118 118
                             if (contract.getPaidAmount() != null) {
119 119
                                 paidAmountI = paidAmountI.add(contract.getPaidAmount());
120
-                                if (contract.getPaidAmount().compareTo(contract.getAmount()) >= 0)
121
-                                    paidYearCountI++;
120
+                                paidYearCountI++;
122 121
                             }
123 122
                         }
124 123
                     }
@@ -156,8 +155,7 @@ public class CmcContractController extends BaseController
156 155
                             monthAmount = monthAmount.add(contract.getAmount());
157 156
                             if (contract.getPaidAmount() != null) {
158 157
                                 paidMonthAmount = paidMonthAmount.add(contract.getPaidAmount());
159
-                                if (contract.getPaidAmount().compareTo(contract.getAmount()) >= 0)
160
-                                    paidMonthCount++;
158
+                                paidMonthCount++;
161 159
                             }
162 160
                         }
163 161
                     }
@@ -198,8 +196,7 @@ public class CmcContractController extends BaseController
198 196
                         monthAmount = monthAmount.add(contract.getAmount());
199 197
                         if (contract.getPaidAmount() != null) {
200 198
                             paidMonthAmount = paidMonthAmount.add(contract.getPaidAmount());
201
-                            if (contract.getPaidAmount().compareTo(contract.getAmount()) >= 0)
202
-                                paidMonthCount++;
199
+                            paidMonthCount++;
203 200
                         }
204 201
                     }
205 202
                 }
@@ -213,8 +210,7 @@ public class CmcContractController extends BaseController
213 210
                     totalAmount = totalAmount.add(contract.getAmount());
214 211
                     if (contract.getPaidAmount() != null) {
215 212
                         paidTotalAmount = paidTotalAmount.add(contract.getPaidAmount());
216
-                        if (contract.getPaidAmount().compareTo(contract.getAmount()) >= 0)
217
-                            paidTotalCount++;
213
+                        paidTotalCount++;
218 214
                     }
219 215
                 }
220 216
             }

+ 4
- 8
oa-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/oa/CmcSubContractController.java Просмотреть файл

@@ -137,8 +137,7 @@ public class CmcSubContractController extends BaseController
137 137
                             yearAmountI = yearAmountI.add(contract.getSubAmount());
138 138
                             if (contract.getPaidAmount() != null) {
139 139
                                 paidAmountI = paidAmountI.add(contract.getPaidAmount());
140
-                                if (contract.getPaidAmount().compareTo(contract.getSubAmount()) >= 0)
141
-                                    paidYearCountI++;
140
+                                paidYearCountI++;
142 141
                             }
143 142
                         }
144 143
                     }
@@ -176,8 +175,7 @@ public class CmcSubContractController extends BaseController
176 175
                             monthAmount = monthAmount.add(contract.getSubAmount());
177 176
                             if (contract.getPaidAmount() != null) {
178 177
                                 paidMonthAmount = paidMonthAmount.add(contract.getPaidAmount());
179
-                                if (contract.getPaidAmount().compareTo(contract.getSubAmount()) >= 0)
180
-                                    paidMonthCount++;
178
+                                paidMonthCount++;
181 179
                             }
182 180
                         }
183 181
                     }
@@ -218,8 +216,7 @@ public class CmcSubContractController extends BaseController
218 216
                         monthAmount = monthAmount.add(contract.getSubAmount());
219 217
                         if (contract.getPaidAmount() != null) {
220 218
                             paidMonthAmount = paidMonthAmount.add(contract.getPaidAmount());
221
-                            if (contract.getPaidAmount().compareTo(contract.getSubAmount()) >= 0)
222
-                                paidMonthCount++;
219
+                            paidMonthCount++;
223 220
                         }
224 221
                     }
225 222
                 }
@@ -233,8 +230,7 @@ public class CmcSubContractController extends BaseController
233 230
                     totalAmount = totalAmount.add(contract.getSubAmount());
234 231
                     if (contract.getPaidAmount() != null) {
235 232
                         paidTotalAmount = paidTotalAmount.add(contract.getPaidAmount());
236
-                        if (contract.getPaidAmount().compareTo(contract.getSubAmount()) >= 0)
237
-                            paidTotalCount++;
233
+                        paidTotalCount++;
238 234
                     }
239 235
                 }
240 236
             }

+ 18
- 0
oa-back/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java Просмотреть файл

@@ -57,6 +57,24 @@ public class RyTask
57 57
         }
58 58
     }
59 59
 
60
+    public void winDump(String dir, String username, String password, String databaseName) throws Exception {
61
+        File file = new File(dir);
62
+        if (!file.exists()) {
63
+            file.mkdir();
64
+        }
65
+        String sqlName = new SimpleDateFormat("yyyyMMdd").format(new Date());
66
+        File datafile = new File(file + File.separator + sqlName + ".sql");
67
+        if (datafile.exists()) {
68
+            System.out.println(sqlName + "文件名已存在,请更换");
69
+            return;
70
+        }
71
+        String mysqldump = "mysqldump -u " + username + " -p" + password + " " + databaseName;
72
+        Process exec = Runtime.getRuntime().exec(new String[]{"cmd", "/c", mysqldump + " > " + datafile.getAbsolutePath()});
73
+        if (exec.waitFor() == 0) {
74
+            System.out.println("数据库备份成功,备份路径为:" + datafile);
75
+        }
76
+    }
77
+
60 78
     public void deleteSysOperLog() {
61 79
         sysOperLogService.deleteOperLogJob();
62 80
         sysLogininforService.deleteLoginInfoJob();

+ 103
- 0
oa-back/ruoyi-system/src/main/java/com/ruoyi/llm/domain/Block.java Просмотреть файл

@@ -0,0 +1,103 @@
1
+package com.ruoyi.llm.domain;
2
+
3
+import java.util.ArrayList;
4
+import java.util.List;
5
+
6
+/**
7
+ * 文档块类型,用于解析 markdown 内容并转换为 Word 格式
8
+ */
9
+public class Block {
10
+    private String type; // para | table | heading | list1 | list2 | imageText | boldOnly | numberedBold
11
+    private String text;
12
+    private List<String> table;
13
+    private Integer headingLevel; // 标题级别(4、5、6 对应 h4、h5、h6)
14
+    private String boldText; // 加粗文本部分
15
+    private String suffixText; // 后缀文本(如冒号及后续内容)
16
+    private String numberPrefix; // 数字前缀,如 "1. "
17
+
18
+    public Block(String t, String x) {
19
+        type = t;
20
+        text = x;
21
+    }
22
+
23
+    public Block(List<String> tbl) {
24
+        type = "table";
25
+        table = new ArrayList<>(tbl);
26
+    }
27
+
28
+    public Block(String t, String x, Integer level) {
29
+        type = t;
30
+        text = x;
31
+        headingLevel = level;
32
+    }
33
+
34
+    public Block(String t, String bold, String suffix) {
35
+        type = t;
36
+        boldText = bold;
37
+        suffixText = suffix;
38
+    }
39
+
40
+    public Block(String t, String numPrefix, String bold, String suffix) {
41
+        type = t;
42
+        numberPrefix = numPrefix;
43
+        boldText = bold;
44
+        suffixText = suffix;
45
+    }
46
+    
47
+    // Getters and Setters
48
+    public String getType() {
49
+        return type;
50
+    }
51
+
52
+    public void setType(String type) {
53
+        this.type = type;
54
+    }
55
+
56
+    public String getText() {
57
+        return text;
58
+    }
59
+
60
+    public void setText(String text) {
61
+        this.text = text;
62
+    }
63
+    
64
+    public List<String> getTable() {
65
+        return table;
66
+    }
67
+
68
+    public void setTable(List<String> table) {
69
+        this.table = table;
70
+    }
71
+    
72
+    public Integer getHeadingLevel() {
73
+        return headingLevel;
74
+    }
75
+
76
+    public void setHeadingLevel(Integer headingLevel) {
77
+        this.headingLevel = headingLevel;
78
+    }
79
+
80
+    public String getBoldText() {
81
+        return boldText;
82
+    }
83
+
84
+    public void setBoldText(String boldText) {
85
+        this.boldText = boldText;
86
+    }
87
+
88
+    public String getSuffixText() {
89
+        return suffixText;
90
+    }
91
+
92
+    public void setSuffixText(String suffixText) {
93
+        this.suffixText = suffixText;
94
+    }
95
+
96
+    public String getNumberPrefix() {
97
+        return numberPrefix;
98
+    }
99
+
100
+    public void setNumberPrefix(String numberPrefix) {
101
+        this.numberPrefix = numberPrefix;
102
+    }
103
+}

+ 114
- 331
oa-back/ruoyi-system/src/main/java/com/ruoyi/llm/service/impl/CmcAgentServiceImpl.java Просмотреть файл

@@ -58,6 +58,7 @@ import org.springframework.stereotype.Service;
58 58
 import org.springframework.web.multipart.MultipartFile;
59 59
 
60 60
 import com.fasterxml.jackson.databind.ObjectMapper;
61
+import com.ruoyi.llm.domain.Block;
61 62
 import com.ruoyi.llm.domain.ChapterStreamResponse;
62 63
 
63 64
 import java.io.*;
@@ -201,7 +202,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
201 202
 
202 203
             // 提取输出文件名(不含路径)
203 204
             String outputFileNameOnly = new File(outputFilename).getName();
204
-            
205
+
205 206
             // 生成详细目录结构(包含二三级标题)并写入文件
206 207
             JSONArray detailedDirectoryTree = generateDetailedDirectory(outputFileNameOnly, agentName, segments);
207 208
             String detailedDirectory = buildDirectoryText(detailedDirectoryTree);
@@ -324,7 +325,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
324 325
 
325 326
         // 保存文档到本地文件系统
326 327
         try (InputStream fileInputStream = new FileInputStream(profilePath + "/" + file.getOriginalFilename());
327
-             XWPFDocument doc = new XWPFDocument(fileInputStream)) {
328
+                XWPFDocument doc = new XWPFDocument(fileInputStream)) {
328 329
             try (FileOutputStream out = new FileOutputStream(profilePath + "/" + file.getOriginalFilename())) {
329 330
                 doc.write(out);
330 331
             }
@@ -732,14 +733,17 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
732 733
 
733 734
         // 输出格式要求
734 735
         prompt.append("【输出格式】:\n");
735
-        prompt.append("请直接输出章节的正文内容(不要包含标题),正文内容为总-分结构,别写成总-分-总结构。");
736
+        prompt.append("请直接输出章节的正文内容(不要包含三级标题),正文内容为总-分结构,别写成总-分-总结构。");
736 737
         prompt.append("【标题编号规范】:\n");
737
-        prompt.append("如果需要在章节内添加子标题,必须严格遵循层级编号格式(如当前章节为6.1.1,则子标题应为6.1.1.1、6.1.1.2、6.1.1.3等)。\n");
738
-        // prompt.append("严禁使用以下编号格式:\n");
739
-        // prompt.append("- 阿拉伯数字加点或顿号(如 1. 2. 3. 或 1、2、3、)\n");
740
-        // prompt.append("- 带括号的数字(如 1)2)3)或 (1)(2)(3))\n");
741
-        // prompt.append("- 中文数字(如 一、二、三 或 第一、第二、第三)\n");
742
-        // prompt.append("- 字母编号(如 A. B. C. 或 a) b) c))\n");
738
+        prompt.append("如果需要在章节内添加子标题,必须严格遵循层级编号格式,最多到第六级标题(如6.1,6.1.1,6.1.1.1,6.1.1.1.1,6.1.1.1.1.1,6.1.1.1.1.1.1),且子标题后应添加换行符。\n");
739
+        prompt.append("各级标题下,正文文字叙述部份的分点说明序号分为六级,\n");
740
+        prompt.append("第一级按阿拉伯数字序号编排,如1,2,3\n");
741
+        prompt.append("第二级按带右圆括号的阿拉伯数字序号编排,如1),2),3)\n");
742
+        prompt.append("第三级按带圆括号的英文小写字母顺序编排(1),(2),(3)\n");
743
+        prompt.append("第四级按英文小写字母顺序编排,如a,b,c\n");
744
+        prompt.append("第五级按带右圆括号的英文小写字母顺序编排,如a),b),c)\n");
745
+        prompt.append("第六级按带圆括号的英文小写字母顺序编排,如(a),(b),(c)\n");
746
+        prompt.append("分点序号前均空两个汉字,无括号后空一个汉字,有括号的紧挨编写内容,所有的括号均为全角符号。\n");
743 747
         prompt.append("所有子标题必须基于父标题编号逐级递增,保持编号体系的一致性。\n\n");
744 748
 
745 749
         prompt.append("请开始生成当前章节 ").append(title).append(" 的详细内容:");
@@ -802,8 +806,24 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
802 806
                 }
803 807
             }
804 808
 
805
-            // 从后往前处理,避免位置偏移
806
-            for (int i = titlePositions.size() - 1; i >= 0; i--) {
809
+            // 按标题在文档中的位置降序排序(与 contents 同步排序),确保从后往前处理避免索引偏移
810
+            if (titlePositions.size() > 1) {
811
+                for (int i = 0; i < titlePositions.size() - 1; i++) {
812
+                    for (int j = i + 1; j < titlePositions.size(); j++) {
813
+                        if (titlePositions.get(i) < titlePositions.get(j)) {
814
+                            int tmpPos = titlePositions.get(i);
815
+                            titlePositions.set(i, titlePositions.get(j));
816
+                            titlePositions.set(j, tmpPos);
817
+                            String tmpContent = contents.get(i);
818
+                            contents.set(i, contents.get(j));
819
+                            contents.set(j, tmpContent);
820
+                        }
821
+                    }
822
+                }
823
+            }
824
+
825
+            // 统一处理所有匹配到的标题:先删除旧内容,再插入新内容
826
+            for (int i = 0; i < titlePositions.size(); i++) {
807 827
                 int titlePos = titlePositions.get(i);
808 828
                 String content = contents.get(i);
809 829
 
@@ -825,15 +845,12 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
825 845
                 // 在标题后面插入新内容
826 846
                 int insertPos = titlePos + 1;
827 847
                 generateWordDocument(content, document, insertPos);
828
-
829
-                // 更新进度
830 848
             }
831
-
832 849
             // 保存文档(此时输入流已关闭)
833 850
             try (FileOutputStream out = new FileOutputStream(absolutePath)) {
834 851
                 document.write(out);
835 852
             }
836
-            
853
+
837 854
             // 标记写入完成
838 855
             processValue = "文档写入完成:100%";
839 856
         } finally {
@@ -846,12 +863,13 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
846 863
     /**
847 864
      * 调用LLM生成目录结构
848 865
      */
849
-    public JSONArray generateDetailedDirectory(String fileName, String agentName, List<TextSegment> segments) throws IOException {
866
+    public JSONArray generateDetailedDirectory(String fileName, String agentName, List<TextSegment> segments)
867
+            throws IOException {
850 868
         StringBuilder sb = new StringBuilder("招标文件内容:\n\n");
851 869
         InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
852 870
         List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
853 871
         embeddingStore.addAll(embeddings, segments);
854
-        Embedding queryEmbedding = embeddingModel.embed("工作大纲/工作范围/招标范围/服务范围").content();
872
+        Embedding queryEmbedding = embeddingModel.embed("技术文件工作大纲要求").content();
855 873
         EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
856 874
                 .queryEmbedding(queryEmbedding)
857 875
                 .maxResults(5)
@@ -865,7 +883,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
865 883
         }
866 884
         // 根据招标文件工作大纲要求生成二级标题
867 885
         sb.append("请基于上述招标文件中提到的")
868
-                .append("工作大纲/工作范围/招标范围/服务范围")
886
+                .append("技术文件工作大纲要求")
869 887
                 .append(",先列出二级章节标题,严格按以下格式,仅输出标题列表:\n")
870 888
                 .append("6.1 XX\n" +
871 889
                         "6.2 XX\n" +
@@ -1106,25 +1124,27 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
1106 1124
     }
1107 1125
 
1108 1126
     // /**
1109
-    //  * 调用LLM生成回答
1110
-    //  *
1111
-    //  * @return
1112
-    //  */
1127
+    // * 调用LLM生成回答
1128
+    // *
1129
+    // * @return
1130
+    // */
1113 1131
     // public String generateAnswer(String prompt) throws IOException {
1114
-    //     ChatModel chatModel = ChatModel.of("https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions")
1115
-    //             .model("qwen3-vl-32b-instruct")
1116
-    //             .apiKey("sk-750a17cc723847f28b31fa1bc17c255c")
1117
-    //             .timeout(Duration.ofSeconds(240))
1118
-    //             .build();
1119
-
1120
-    //     List<ChatMessage> messages = new ArrayList<>();
1121
-    //     messages.add(ChatMessage.ofUser(prompt));
1122
-    //     ChatSession chatSession = InMemoryChatSession.builder().messages(messages).build();
1123
-
1124
-    //     Prompt prompt1 = Prompt.of(prompt).attrPut("session", chatSession);
1125
-    //     ChatResponse response = chatModel.prompt(prompt1).call();
1126
-    //     String content = response.lastChoice().getMessage().getContent();
1127
-    //     return content;
1132
+    // ChatModel chatModel =
1133
+    // ChatModel.of("https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions")
1134
+    // .model("qwen3-vl-32b-instruct")
1135
+    // .apiKey("sk-750a17cc723847f28b31fa1bc17c255c")
1136
+    // .timeout(Duration.ofSeconds(240))
1137
+    // .build();
1138
+
1139
+    // List<ChatMessage> messages = new ArrayList<>();
1140
+    // messages.add(ChatMessage.ofUser(prompt));
1141
+    // ChatSession chatSession =
1142
+    // InMemoryChatSession.builder().messages(messages).build();
1143
+
1144
+    // Prompt prompt1 = Prompt.of(prompt).attrPut("session", chatSession);
1145
+    // ChatResponse response = chatModel.prompt(prompt1).call();
1146
+    // String content = response.lastChoice().getMessage().getContent();
1147
+    // return content;
1128 1148
     // }
1129 1149
 
1130 1150
     /**
@@ -1172,8 +1192,9 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
1172 1192
     /**
1173 1193
      * 流式调用LLM生成回答 - 使用Flux(包含usage信息)
1174 1194
      * 
1175
-     * @param prompt 提示词
1176
-     * @param tokenUsage 用于收集token用量的数组 [promptTokens, completionTokens, totalTokens]
1195
+     * @param prompt     提示词
1196
+     * @param tokenUsage 用于收集token用量的数组 [promptTokens, completionTokens,
1197
+     *                   totalTokens]
1177 1198
      * @return Flux<String> 流式输出(包含内容)
1178 1199
      */
1179 1200
     public Flux<String> generateAnswerFluxWithUsage(String prompt, AtomicLong[] tokenUsage) {
@@ -1186,26 +1207,29 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
1186 1207
         ChatSession chatSession = InMemoryChatSession.builder().messages(messages).build();
1187 1208
 
1188 1209
         Prompt prompt1 = Prompt.of(prompt).attrPut("session", chatSession);
1189
-        
1210
+
1190 1211
         // 估算prompt的token数量(中文大约1个字符=1个token)
1191 1212
         if (tokenUsage != null && tokenUsage.length >= 3 && tokenUsage[0].get() == 0) {
1192 1213
             tokenUsage[0].set((long) prompt.length());
1193 1214
         }
1194
-        
1215
+
1195 1216
         return chatModel.prompt(prompt1).stream()
1196 1217
                 .doOnNext(resp -> {
1197 1218
                     if (tokenUsage != null && tokenUsage.length >= 3) {
1198 1219
                         try {
1199 1220
                             // 优先使用vLLM返回的usage信息(在最后一个chunk中)
1200 1221
                             AiUsage usage = resp.getUsage();
1201
-                            if (usage != null) {                                
1222
+                            if (usage != null) {
1202 1223
                                 Long pt = usage.promptTokens();
1203 1224
                                 Long ct = usage.completionTokens();
1204 1225
                                 Long tt = usage.totalTokens();
1205
-                                
1206
-                                if (pt > 0) tokenUsage[0].set(pt);
1207
-                                if (ct > 0) tokenUsage[1].set(ct);
1208
-                                if (tt > 0) tokenUsage[2].set(tt);
1226
+
1227
+                                if (pt > 0)
1228
+                                    tokenUsage[0].set(pt);
1229
+                                if (ct > 0)
1230
+                                    tokenUsage[1].set(ct);
1231
+                                if (tt > 0)
1232
+                                    tokenUsage[2].set(tt);
1209 1233
                             } else {
1210 1234
                                 // 如果没有usage信息(中间chunk),估算completion tokens
1211 1235
                                 String content = resp.getContent();
@@ -1348,69 +1372,6 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
1348 1372
         }
1349 1373
     }
1350 1374
 
1351
-    /**
1352
-     * 写入章节内容
1353
-     * 
1354
-     * @return
1355
-     */
1356
-    public void writeContent(String content, List<String> titles, String absolutePath) throws IOException {
1357
-        String[] contentLines = content.split("\n");
1358
-        Map<String, String> map = new HashMap<>();
1359
-        File file = new File(absolutePath);
1360
-        try (FileInputStream fileInputStream = new FileInputStream(file);
1361
-             XWPFDocument document = new XWPFDocument(fileInputStream)) {
1362
-            for (int i = 0; i < titles.size(); i++) {
1363
-                int startIndex = Arrays.asList(contentLines).indexOf(titles.get(i));
1364
-                StringBuilder text = new StringBuilder();
1365
-                if (startIndex >= 0) {
1366
-                    if (i < titles.size() - 1) {
1367
-                        int endIndex = Arrays.asList(contentLines).indexOf(titles.get(i + 1));
1368
-                        for (int c = startIndex + 1; c < endIndex; c++) {
1369
-                            text.append(contentLines[c]).append("\n\n");
1370
-                        }
1371
-                    } else {
1372
-                        if (startIndex + 1 < contentLines.length) {
1373
-                            for (int c = startIndex + 1; c < contentLines.length; c++) {
1374
-                                text.append(contentLines[c]).append("\n\n");
1375
-                            }
1376
-                        }
1377
-                    }
1378
-                } else
1379
-                    text.append(content);
1380
-                map.put(titles.get(i), text.toString());
1381
-            }
1382
-
1383
-            List<Integer> positions = new ArrayList<>();
1384
-            List<String> contents = new ArrayList<>();
1385
-            List<XWPFParagraph> paragraphs = document.getParagraphs();
1386
-
1387
-            for (int i = 0; i < paragraphs.size(); i++) {
1388
-                XWPFParagraph paragraph = paragraphs.get(i);
1389
-                String paragraphText = paragraph.getText().trim();
1390
-                for (String title : titles) {
1391
-                    if (paragraphText.equals(toParagraphText(title))) {
1392
-                        positions.add(i);
1393
-                        contents.add(map.get(title));
1394
-                        break;
1395
-                    }
1396
-                }
1397
-            }
1398
-
1399
-            for (int i = positions.size() - 1; i >= 0; i--) {
1400
-                int insertPos = positions.get(i) + 1;
1401
-                XmlCursor xmlCursor = paragraphs.get(insertPos).getCTP().newCursor();
1402
-                XWPFParagraph contentParagraph = document.insertNewParagraph(xmlCursor);
1403
-                contentParagraph.setStyle("1");
1404
-                XWPFRun run = contentParagraph.createRun();
1405
-                run.setText(contents.get(i));
1406
-            }
1407
-
1408
-            try (FileOutputStream out = new FileOutputStream(absolutePath)) {
1409
-                document.write(out);
1410
-            }
1411
-        }
1412
-    }
1413
-
1414 1375
     /**
1415 1376
      * 将带编号的标题转为 Word 段落文本(自动编号模板下段落 text 不含编号前缀)
1416 1377
      */
@@ -1552,7 +1513,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
1552 1513
         List<XWPFParagraph> currentPath = new ArrayList<>();
1553 1514
 
1554 1515
         try (InputStream fileInputStream = new FileInputStream(filename);
1555
-             XWPFDocument document = new XWPFDocument(fileInputStream)) {
1516
+                XWPFDocument document = new XWPFDocument(fileInputStream)) {
1556 1517
 
1557 1518
             for (XWPFParagraph paragraph : document.getParagraphs()) {
1558 1519
                 String text = paragraph.getText();
@@ -1619,7 +1580,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
1619 1580
     public String extractTitles(String filename) throws IOException {
1620 1581
         StringBuilder subTitles = new StringBuilder();
1621 1582
         try (InputStream fileInputStream = new FileInputStream(filename);
1622
-             XWPFDocument document = new XWPFDocument(fileInputStream)) {
1583
+                XWPFDocument document = new XWPFDocument(fileInputStream)) {
1623 1584
             for (XWPFParagraph paragraph : document.getParagraphs()) {
1624 1585
                 String text = paragraph.getText().trim();
1625 1586
                 int level = getDocxOutlineLevel(paragraph, document);
@@ -2027,94 +1988,27 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
2027 1988
             List<String> tableLines = new ArrayList<>();
2028 1989
 
2029 1990
             // 将内容解析为块(普通段落/表格/标题/列表项等),然后倒序插入,确保顺序正确
2030
-            class Block {
2031
-                String type; // para | table | h4 | list1 | list2 | imageText | boldOnly | numberedBold
2032
-                String text;
2033
-                List<String> table;
2034
-                Integer h4Index; // 预计算的四级标题编号
2035
-                Integer list1Index; // 预计算的一级列表编号 1),2),3)
2036
-                Integer list2Index; // 预计算的二级列表编号 (1),(2),(3)
2037
-                String boldText; // 加粗文本部分
2038
-                String suffixText; // 后缀文本(如冒号及后续内容)
2039
-                String numberPrefix; // 数字前缀,如 "1. "
2040
-
2041
-                Block(String t, String x) {
2042
-                    type = t;
2043
-                    text = x;
2044
-                }
2045
-
2046
-                Block(List<String> tbl) {
2047
-                    type = "table";
2048
-                    table = new ArrayList<>(tbl);
2049
-                }
2050
-
2051
-                Block(String t, String x, Integer h4Idx, Integer l1Idx, Integer l2Idx) {
2052
-                    type = t;
2053
-                    text = x;
2054
-                    h4Index = h4Idx;
2055
-                    list1Index = l1Idx;
2056
-                    list2Index = l2Idx;
2057
-                }
2058
-
2059
-                Block(String t, String bold, String suffix) {
2060
-                    type = t;
2061
-                    boldText = bold;
2062
-                    suffixText = suffix;
2063
-                }
2064
-
2065
-                Block(String t, String numPrefix, String bold, String suffix) {
2066
-                    type = t;
2067
-                    numberPrefix = numPrefix;
2068
-                    boldText = bold;
2069
-                    suffixText = suffix;
2070
-                }
2071
-            }
2072 1991
             List<Block> blocks = new ArrayList<>();
2073 1992
 
2074
-            // 预计算编号(按正序扫描)
2075
-            int h4Counter = 0;
2076
-            int list1Counter = 0;
2077
-            int list2Counter = 0;
2078
-            String previousLine = ""; // 记录上一行内容
2079
-            boolean wasListItem = false; // 记录上一行是否是列表项
2080
-
2081 1993
             for (String raw : lines) {
2082 1994
                 String line = raw.trim();
2083
-                boolean isCurrentListItem = false;
2084 1995
                 if (line.isEmpty()) {
2085 1996
                     blocks.add(new Block("para", "")); // 空段落
2086
-                    // 空行后重置列表编号
2087
-                    if (!wasListItem) {
2088
-                        list1Counter = 0;
2089
-                        list2Counter = 0;
2090
-                    }
2091
-                    previousLine = line;
2092
-                    wasListItem = false;
2093 1997
                     continue;
2094 1998
                 }
2095 1999
 
2096
-                // 检查上一行是否以中文冒号结尾,如果是则重置列表编号
2097
-                if (previousLine.endsWith(":") || previousLine.endsWith(":")) {
2098
-                    list1Counter = 0;
2099
-                    list2Counter = 0;
2100
-                }
2101
-
2102
-                if (line.startsWith("#")) {
2103
-                    // 跳过前三级标题
2104
-                    previousLine = line;
2105
-                    wasListItem = false;
2000
+                if (line.matches("^#{4,6}\\s+.*")) {
2001
+                    // 四、五、六级标题(####/#####/######),去掉编号前缀,由Word自动编号
2002
+                    int hashCount = 0;
2003
+                    while (hashCount < line.length() && line.charAt(hashCount) == '#') {
2004
+                        hashCount++;
2005
+                    }
2006
+                    String title = line.substring(hashCount).trim();
2007
+                    title = title.replaceFirst("^\\d+(\\.\\d+)*\\.?\\s*", "");
2008
+                    blocks.add(new Block("heading", title, hashCount));
2009
+                } else if (line.startsWith("#")) {
2010
+                    // 跳过前三级标题(#、##、###)
2106 2011
                     continue;
2107
-                } else if (line.startsWith("####")) {
2108
-                    // 四级标题,编号采用预计算,保证倒序插入后视觉顺序仍递增
2109
-                    String title = line.replace("####", "").trim();
2110
-                    title = title.replaceFirst("^\\d+\\.\\s*", "");
2111
-                    h4Counter++;
2112
-                    // 碰到新小节,重置列表编号
2113
-                    list1Counter = 0;
2114
-                    list2Counter = 0;
2115
-                    blocks.add(new Block("h4", title, h4Counter, null, null));
2116
-                    previousLine = line;
2117
-                    wasListItem = false;
2118 2012
                 } else if (line.startsWith("|")) {
2119 2013
                     // 表格模式开始
2120 2014
                     if (!inTable) {
@@ -2131,102 +2025,9 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
2131 2025
                     }
2132 2026
                     // 当前行按普通段落处理
2133 2027
                     blocks.add(new Block("para", line));
2134
-                    // 非列表项,重置计数器
2135
-                    if (wasListItem) {
2136
-                        list1Counter = 0;
2137
-                        list2Counter = 0;
2138
-                    }
2139
-                    wasListItem = false;
2140 2028
                 } else if (inTable) {
2141
-                    tableLines.add(line);
2142
-                } else if (line.startsWith("- **")) {
2143
-                    // 一级加粗列表项,编号为 1),2),3) —— 预计算编号
2144
-                    if (!wasListItem) {
2145
-                        list1Counter = 0;
2146
-                        list2Counter = 0;
2147
-                    }
2148
-                    String listItem = line.replaceFirst("- \\*\\*", "").replace("**", "");
2149
-                    listItem = listItem.replaceFirst("^\\*\\*", "").replace("**:", ":");
2150
-                    list1Counter++;
2151
-                    // 一级列表开始时,重置二级列表编号
2152
-                    list2Counter = 0;
2153
-                    Block b = new Block("list1", listItem, null, list1Counter, null);
2154
-                    blocks.add(b);
2155
-                    isCurrentListItem = true;
2156
-                } else if (line.matches("^\\s+-\\s+\\*\\*.*")) {
2157
-                    // 二级加粗列表项,编号为 (1),(2),(3) —— 预计算编号
2158
-                    String listItem = line.replaceFirst("^\\s+-\\s+\\*\\*", "").replace("**", "");
2159
-                    listItem = listItem.replaceFirst("^\\*\\*", "").replace("**:", ":");
2160
-                    list2Counter++;
2161
-                    Block b = new Block("list2", listItem, null, null, list2Counter);
2162
-                    blocks.add(b);
2163
-                    isCurrentListItem = true;
2164
-                } else if (line.matches("^\\*\\s+.*") && line.contains("**")) {
2165
-                    // 列表项格式:* **文本** 或 *   **文本**(*后跟一个或多个空格,然后加粗)
2166
-                    if (!wasListItem) {
2167
-                        list1Counter = 0;
2168
-                        list2Counter = 0;
2169
-                    }
2170
-                    // 提取 ** 之间的内容
2171
-                    Pattern starBoldPattern = Pattern.compile("^\\*\\s+\\*\\*([^*]+)\\*\\*(.*)$");
2172
-                    Matcher sbm = starBoldPattern.matcher(line);
2173
-                    if (sbm.matches()) {
2174
-                        String bold = sbm.group(1);
2175
-                        String suf = sbm.group(2);
2176
-                        if (suf == null) suf = "";
2177
-                        list1Counter++;
2178
-                        list2Counter = 0;
2179
-                        Block b = new Block("list1", bold + suf, null, list1Counter, null);
2180
-                        blocks.add(b);
2181
-                        isCurrentListItem = true;
2182
-                    } else {
2183
-                        // 不匹配 * **文本** 格式,按普通列表项处理
2184
-                        String listItem = line.replaceFirst("^\\*\\s+", "");
2185
-                        list1Counter++;
2186
-                        list2Counter = 0;
2187
-                        Block b = new Block("list1", listItem, null, list1Counter, null);
2188
-                        blocks.add(b);
2189
-                        isCurrentListItem = true;
2190
-                    }
2191
-                } else if (line.matches("^\\*\\s+.*")) {
2192
-                    // 普通列表项,以 "* " 开头(不带加粗)
2193
-                    if (!wasListItem) {
2194
-                        list1Counter = 0;
2195
-                        list2Counter = 0;
2196
-                    }
2197
-                    String listItem = line.replaceFirst("^\\*\\s+", "");
2198
-                    list1Counter++;
2199
-                    list2Counter = 0;
2200
-                    Block b = new Block("list1", listItem, null, list1Counter, null);
2201
-                    blocks.add(b);
2202
-                    isCurrentListItem = true;
2203
-                } else if (line.matches("^\\s+\\*\\s+.*")) {
2204
-                    // 缩进的普通列表项,以 "  * " 开头(二级列表)
2205
-                    String listItem = line.replaceFirst("^\\s+\\*\\s+", "");
2206
-                    list2Counter++;
2207
-                    Block b = new Block("list2", listItem, null, null, list2Counter);
2208
-                    blocks.add(b);
2209
-                    isCurrentListItem = true;
2210
-                } else if (line.matches("^-\\s+.*") && !line.matches("^-\\s+\\*\\*.*")) {
2211
-                    // 普通列表项,以 "- " 开头(不带加粗)
2212
-                    if (!wasListItem) {
2213
-                        list1Counter = 0;
2214
-                        list2Counter = 0;
2215
-                    }
2216
-                    String listItem = line.replaceFirst("^-\\s+", "");
2217
-                    list1Counter++;
2218
-                    list2Counter = 0;
2219
-                    Block b = new Block("list1", listItem, null, list1Counter, null);
2220
-                    blocks.add(b);
2221
-                    isCurrentListItem = true;
2029
+                    tableLines.add(line);                
2222 2030
                 } else {
2223
-                    // 非列表项,重置计数器
2224
-                    if (wasListItem) {
2225
-                        list1Counter = 0;
2226
-                        list2Counter = 0;
2227
-                    }
2228
-                    wasListItem = false;
2229
-                    // 匹配格式:1. **文本**(数字前缀+加粗)
2230 2031
                     // 匹配格式:1. **文本**(数字前缀+加粗)
2231 2032
                     Pattern numberedBoldPattern = Pattern.compile("^(\\d+\\.\\s+)\\*\\*([^*]+)\\*\\*(.*)$");
2232 2033
                     Matcher nbm = numberedBoldPattern.matcher(line);
@@ -2237,11 +2038,10 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
2237 2038
                         if (suf == null)
2238 2039
                             suf = "";
2239 2040
                         blocks.add(new Block("numberedBold", numPrefix, bold, suf));
2240
-                        previousLine = line;
2241 2041
                         continue;
2242 2042
                     }
2243 2043
                     // 匹配格式:**文本**
2244
-                    Pattern boldOnlyPattern = Pattern.compile("^\\*\\*([^*]+)\\*\\*(.*)$");
2044
+                    Pattern boldOnlyPattern = Pattern.compile("\\*\\*([^*]+)\\*\\*(.*)");
2245 2045
                     Matcher boldMatcher = boldOnlyPattern.matcher(line);
2246 2046
                     if (boldMatcher.matches()) {
2247 2047
                         String boldText = boldMatcher.group(1); // "数据校验"
@@ -2249,7 +2049,6 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
2249 2049
                         if (suffix == null)
2250 2050
                             suffix = "";
2251 2051
                         blocks.add(new Block("boldOnly", boldText, suffix));
2252
-                        previousLine = line;
2253 2052
                         continue;
2254 2053
                     }
2255 2054
                     // 如果不是 boldOnly 格式,继续后续处理
@@ -2264,10 +2063,6 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
2264 2063
                         blocks.add(new Block("para", line));
2265 2064
                     }
2266 2065
                 }
2267
-
2268
-                // 更新上一行记录
2269
-                previousLine = line;
2270
-                wasListItem = isCurrentListItem;
2271 2066
             }
2272 2067
             // 收尾:如果文件最后是表格仍未输出
2273 2068
             if (inTable && !tableLines.isEmpty()) {
@@ -2287,48 +2082,34 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
2287 2082
                 }
2288 2083
                 XmlCursor cursor = paragraphs.get(insertPos).getCTP().newCursor();
2289 2084
 
2290
-                switch (b.type) {
2291
-                    case "h4": {
2292
-                        XWPFParagraph p = document.insertNewParagraph(cursor);
2293
-                        XWPFRun run = p.createRun();
2294
-                        run.setText(b.text);
2295
-                        run.setBold(true);
2296
-                        run.setFontSize(14);
2297
-                        break;
2298
-                    }
2299
-                    case "list1": {
2300
-                        XWPFParagraph p = document.insertNewParagraph(cursor);
2301
-                        XWPFRun run = p.createRun();
2302
-                        int num = (b.list1Index != null ? b.list1Index : 1);
2303
-                        run.setText(num + ") " + b.text);
2304
-                        run.setFontSize(12);
2305
-                        break;
2306
-                    }
2307
-                    case "list2": {
2085
+                switch (b.getType()) {
2086
+                    case "heading": {
2308 2087
                         XWPFParagraph p = document.insertNewParagraph(cursor);
2088
+                        // 使用模板文件的对应等级标题样式,由Word自动编号
2089
+                        if (b.getHeadingLevel() != null) {
2090
+                            p.setStyle(String.valueOf(b.getHeadingLevel() + 1));
2091
+                        }
2309 2092
                         XWPFRun run = p.createRun();
2310
-                        int num = (b.list2Index != null ? b.list2Index : 1);
2311
-                        run.setText("   (" + num + ") " + b.text);
2312
-                        run.setFontSize(12);
2093
+                        run.setText(b.getText());
2313 2094
                         break;
2314 2095
                     }
2315 2096
                     case "numberedBold": {
2316 2097
                         XWPFParagraph p = document.insertNewParagraph(cursor);
2317 2098
                         // 数字前缀(不加粗)
2318
-                        if (b.numberPrefix != null) {
2099
+                        if (b.getNumberPrefix() != null) {
2319 2100
                             XWPFRun r1 = p.createRun();
2320
-                            r1.setText(b.numberPrefix);
2101
+                            r1.setText(b.getNumberPrefix());
2321 2102
                             r1.setFontSize(12);
2322 2103
                         }
2323 2104
                         // 加粗文本
2324 2105
                         XWPFRun r2 = p.createRun();
2325
-                        r2.setText(b.boldText != null ? b.boldText : "");
2106
+                        r2.setText(b.getBoldText() != null ? b.getBoldText() : "");
2326 2107
                         r2.setFontSize(12);
2327 2108
                         r2.setBold(true);
2328 2109
                         // 后缀(不加粗)
2329
-                        if (b.suffixText != null && !b.suffixText.isEmpty()) {
2110
+                        if (b.getSuffixText() != null && !b.getSuffixText().isEmpty()) {
2330 2111
                             XWPFRun r3 = p.createRun();
2331
-                            r3.setText(b.suffixText);
2112
+                            r3.setText(b.getSuffixText());
2332 2113
                             r3.setFontSize(12);
2333 2114
                         }
2334 2115
                         break;
@@ -2339,14 +2120,14 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
2339 2120
 
2340 2121
                         // 加粗文本部分
2341 2122
                         XWPFRun run1 = p.createRun();
2342
-                        run1.setText(b.boldText);
2123
+                        run1.setText(b.getBoldText());
2343 2124
                         run1.setFontSize(12);
2344 2125
                         run1.setBold(true);
2345 2126
 
2346 2127
                         // 后缀部分(不加粗,如冒号及后续内容,如果有的话)
2347
-                        if (b.suffixText != null && !b.suffixText.isEmpty()) {
2128
+                        if (b.getSuffixText() != null && !b.getSuffixText().isEmpty()) {
2348 2129
                             XWPFRun run2 = p.createRun();
2349
-                            run2.setText(b.suffixText);
2130
+                            run2.setText(b.getSuffixText());
2350 2131
                             run2.setFontSize(12);
2351 2132
                         }
2352 2133
                         break;
@@ -2354,7 +2135,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
2354 2135
                     case "imageText": {
2355 2136
                         XWPFParagraph p = document.insertNewParagraph(cursor);
2356 2137
                         XWPFRun run = p.createRun();
2357
-                        run.setText(b.text);
2138
+                        run.setText(b.getText());
2358 2139
                         run.setFontSize(12);
2359 2140
                         run.setItalic(true);
2360 2141
                         run.setColor("808080");
@@ -2364,7 +2145,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
2364 2145
                         // 解析 markdown 表格并插入在 cursor 位置
2365 2146
                         List<String[]> tableData = new ArrayList<>();
2366 2147
                         Pattern sep = Pattern.compile("^\\|\\s*(-+\\s*\\|\\s*)*-+\\s*\\|?$");
2367
-                        for (String tl : b.table) {
2148
+                        for (String tl : b.getTable()) {
2368 2149
                             if (sep.matcher(tl).matches())
2369 2150
                                 continue;
2370 2151
                             String t = tl.trim();
@@ -2395,6 +2176,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
2395 2176
                                     XWPFTableCell cell = tr.getCell(c);
2396 2177
                                     cell.removeParagraph(0);
2397 2178
                                     XWPFParagraph cp = cell.addParagraph();
2179
+                                    cp.setIndentationFirstLine(0);
2398 2180
                                     String cellText = rowData[c];
2399 2181
                                     if (r == 0) {
2400 2182
                                         // 表头行整体加粗
@@ -2435,15 +2217,15 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
2435 2217
                     case "para":
2436 2218
                     default: {
2437 2219
                         XWPFParagraph p = document.insertNewParagraph(cursor);
2438
-                        if (b.text != null && b.text.contains("**")) {
2220
+                        if (b.getText() != null && b.getText().contains("**")) {
2439 2221
                             // 解析行内 **文本** 加粗
2440
-                            Matcher m = Pattern.compile("\\*\\*([^*]+)\\*\\*").matcher(b.text);
2222
+                            Matcher m = Pattern.compile("\\*\\*([^*]+)\\*\\*").matcher(b.getText());
2441 2223
                             int lastEnd = 0;
2442 2224
                             while (m.find()) {
2443 2225
                                 // 加粗前的普通文本
2444 2226
                                 if (m.start() > lastEnd) {
2445 2227
                                     XWPFRun run = p.createRun();
2446
-                                    run.setText(b.text.substring(lastEnd, m.start()));
2228
+                                    run.setText(b.getText().substring(lastEnd, m.start()));
2447 2229
                                     run.setFontSize(12);
2448 2230
                                 }
2449 2231
                                 // 加粗文本
@@ -2454,15 +2236,15 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
2454 2236
                                 lastEnd = m.end();
2455 2237
                             }
2456 2238
                             // 剩余文本
2457
-                            if (lastEnd < b.text.length()) {
2239
+                            if (lastEnd < b.getText().length()) {
2458 2240
                                 XWPFRun run = p.createRun();
2459
-                                run.setText(b.text.substring(lastEnd));
2241
+                                run.setText(b.getText().substring(lastEnd));
2460 2242
                                 run.setFontSize(12);
2461 2243
                             }
2462 2244
                         } else {
2463 2245
                             XWPFRun run = p.createRun();
2464
-                            if (b.text != null) {
2465
-                                run.setText(b.text);
2246
+                            if (b.getText() != null) {
2247
+                                run.setText(b.getText());
2466 2248
                             }
2467 2249
                             run.setFontSize(12);
2468 2250
                         }
@@ -2505,14 +2287,15 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
2505 2287
         SseEmitter emitter = new SseEmitter(600000L);
2506 2288
 
2507 2289
         // 设置回调
2508
-        emitter.onCompletion(() -> {});
2290
+        emitter.onCompletion(() -> {
2291
+        });
2509 2292
         emitter.onTimeout(() -> {
2510 2293
             emitter.complete();
2511 2294
         });
2512 2295
         emitter.onError(e -> System.err.println("SSE连接错误: " + e.getMessage()));
2513 2296
 
2514 2297
         // 在主线程中预先获取用户ID,避免异步线程中丢失SecurityContext
2515
-        
2298
+
2516 2299
         Long userId = SecurityUtils.getUserId();
2517 2300
 
2518 2301
         // 使用独立线程池处理,避免阻塞主线程
@@ -2626,7 +2409,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
2626 2409
                                     try {
2627 2410
                                         if (content != null && !content.isEmpty()) {
2628 2411
                                             fullContent.append(content);
2629
-                                            
2412
+
2630 2413
                                             // 逐字发送到前端,并带上当前已累计的 token 用量
2631 2414
                                             ChapterStreamResponse response = ChapterStreamResponse.content(
2632 2415
                                                     title, content, false, finalChapterIndex, totalChapters);
@@ -2667,7 +2450,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService {
2667 2450
                                         completeResponse.setPromptTokens(tokenUsage[0].get());
2668 2451
                                         completeResponse.setCompletionTokens(tokenUsage[1].get());
2669 2452
                                         completeResponse.setTotalTokens(tokenUsage[2].get());
2670
-                                        
2453
+
2671 2454
                                         emitter.send(SseEmitter.event()
2672 2455
                                                 .name("chapter")
2673 2456
                                                 .data(objectMapper.writeValueAsString(completeResponse)));

+ 6
- 0
oa-ui/src/utils/deleteResource.js Просмотреть файл

@@ -162,6 +162,12 @@ const apiEndpoints = [
162 162
       '/oa/transfer/:id',
163 163
     ]
164 164
   },
165
+  {
166
+    procDefName: '招聘审批',
167
+    apiUrl: [
168
+      '/oa/recruit/:id',
169
+    ]
170
+  },
165 171
 ]
166 172
 
167 173
 // 编写一个方法来处理删除请求,并同时发送所有API请求  

Загрузка…
Отмена
Сохранить