Sfoglia il codice sorgente

test python的相关模型调用2

chenendian 1 settimana fa
parent
commit
5246e2b9b3

+ 115 - 43
siwei-modules/siwei-apply/src/main/java/com/siwei/apply/controller/ChenController.java

@@ -3,7 +3,6 @@ package com.siwei.apply.controller;
 import com.siwei.apply.domain.res.YdysyxzRes;
 import com.siwei.apply.domain.vo.YdysyxzVo;
 import com.siwei.apply.service.YdysyxzService;
-import com.siwei.apply.utils.FixedThreadUtil;
 import com.siwei.common.core.domain.R;
 import com.siwei.common.core.utils.StringUtils;
 import com.siwei.common.core.web.controller.BaseController;
@@ -11,11 +10,15 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import java.io.BufferedReader;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 /**
  * 用地预审与选址 控制器
@@ -24,6 +27,17 @@ import java.util.Map;
 @RestController
 @RequestMapping("/chen")
 public class ChenController extends BaseController {
+
+    /**
+     * OCR 脚本同步执行的最长等待时间(分钟)
+     */
+    private static final long OCR_TIMEOUT_MINUTES = 10;
+
+    /**
+     * 返回给前端的异常输出最大长度
+     */
+    private static final int MAX_ERROR_LENGTH = 900;
+
     @Autowired
     private YdysyxzService ydysyxzService;
 
@@ -99,53 +113,111 @@ public class ChenController extends BaseController {
     }
 
 
+    /**
+     * 调用 python 脚本做图像识别(OCR),同步执行并返回识别内容
+     * <p>
+     * 等价于命令行:
+     * python "D:\Tie-Ortho\ocr-tests\20261012-land-application\run_ocr.py"
+     *        "C:\Users\siwei\Desktop\tp_test\疑似违法用地情况说明.pdf" --mode free
+     * <p>
+     * 注意:OCR 属于耗时任务,这里同步等待(上限 OCR_TIMEOUT_MINUTES 分钟),
+     * 会一直占用一个 HTTP 工作线程,网关/前端需要相应放宽超时时间。
+     */
     @GetMapping("/testPython")
-    public R<String> testPython() {
+    public R<Map<String, Object>> testPython() {
+        // python 解释器(conda 虚拟环境)
+        String pythonHome = "E:\\anaconda\\envs\\py310\\python.exe";
+        // 识别脚本
+        String finalPyPath = "D:\\Tie-Ortho\\ocr-tests\\20261012-land-application\\run_ocr.py";
+        // 待识别的图片/文件
+        String imgPath = "C:\\Users\\siwei\\Desktop\\tp_test\\用地规划设计条件.jpg";
+        // 识别模式
+        String mode = "free";
+
+        Process process = null;
+        BufferedReader reader = null;
         try {
-            //C:\Users\siwei>python "D:\Tie-Ortho\ocr-tests\20261012-land-application\run_ocr.py"  "C:\Users\siwei\Desktop\tp_test\疑似违法用地情况说明.pdf"   --mode free
-            String retString = "测试";
-            String pythonHome = "E:\\anaconda\\envs\\py310\\python.exe";
-            String finalPyPath = "D:\\Tie-Ortho\\ocr-tests\\20261012-land-application\\run_ocr.py";
-            String imgPath = "C:\\Users\\siwei\\Desktop\\tp_test\\用地规划设计条件.jpg";
-            FixedThreadUtil.FIXED_THREAD_POOL.execute(() -> {
-                try {
-                    // 更新状态为运行中
-                    //TODO command ProcessBuilder
-                    ProcessBuilder pb = new ProcessBuilder(pythonHome, finalPyPath, imgPath, "", "", "", "");
-                    pb.redirectErrorStream(true);
-                    Process process = null;
-                    process = pb.start();
-                    BufferedReader reader = new BufferedReader(
-                            new InputStreamReader(process.getInputStream()));
-                    String line;
-                    ArrayList<String> datas = new ArrayList<>();
-                    while ((line = reader.readLine()) != null) {
-                        if (StringUtils.isNotEmpty(line)) {
-                            if (line.contains("||")) {
-                                datas.add(line.split("ERROR")[0]);
-                            }
-                        }
-                    }
-                    if (datas.size() > 0) {
-                        // 更新状态为成功
-                        for (String cur : datas) {
-                            String[] args = cur.split("\\|\\|");
-                            String id = StringUtils.getUUID();
-                            // 处理文件路径
-                            args[1] = StringUtils.replaceEach(args[1], new String[]{"D:", "\\"}, new String[]{"", "/"});
-                            // 插入制图产品表
-                            //thematicMapper.addDetails(id, uuid, args[0], args[1], args[2]);
-                            System.out.println("制图产品表插入成功:" + args[0] + " " + args[1] + " " + args[2]);
-                        }
-                    }
-                } catch (IOException e) {
-                    e.printStackTrace();
+            // 注意:不要再传多余的空串参数,脚本按 "脚本 + 文件 + --mode + 模式" 的约定调用
+            ProcessBuilder pb = new ProcessBuilder(pythonHome, finalPyPath, imgPath, "--mode", mode);
+            // stderr 合并进 stdout,避免只开一个流时子进程写满 stderr 缓冲区而阻塞
+            pb.redirectErrorStream(true);
+            // 强制 python 以 UTF-8 输出,解决 Windows 默认 GBK 导致的中文乱码
+            pb.environment().put("PYTHONIOENCODING", "utf-8");
+            // 以脚本所在目录作为工作目录,脚本内的相对路径才不会找不到
+            File scriptFile = new File(finalPyPath);
+            File scriptDir = scriptFile.getParentFile();
+            if (scriptDir != null && scriptDir.exists()) {
+                pb.directory(scriptDir);
+            }
+
+            process = pb.start();
+
+            // 按 UTF-8 读取输出(原写法用平台默认编码,中文必乱码)
+            reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
+            StringBuilder output = new StringBuilder();
+            List<String> items = new ArrayList<>();
+            String line;
+            while ((line = reader.readLine()) != null) {
+                if (StringUtils.isBlank(line)) {
+                    continue;
                 }
-            });
-            return R.ok(retString);
+                output.append(line).append("\n");
+                // 兼容脚本用 "||" 分隔的结构化输出行
+                if (line.contains("||")) {
+                    items.add(line.split("ERROR")[0]);
+                }
+            }
+
+            // 必须等待进程退出并校验退出码,否则脚本报错也会被当成成功
+            boolean finished = process.waitFor(OCR_TIMEOUT_MINUTES, TimeUnit.MINUTES);
+            if (!finished) {
+                process.destroyForcibly();
+                return R.fail("OCR 执行超时(超过 " + OCR_TIMEOUT_MINUTES + " 分钟已强制终止)");
+            }
+            int exitCode = process.exitValue();
+            if (exitCode != 0) {
+                return R.fail("OCR 执行失败,exitCode=" + exitCode + ":" + truncate(output.toString()));
+            }
+
+            String content = output.toString().trim();
+            if (StringUtils.isBlank(content)) {
+                return R.fail("python 脚本未返回任何识别内容");
+            }
+
+            Map<String, Object> data = new HashMap<>();
+            data.put("exitCode", exitCode);
+            data.put("content", content);   // 脚本完整输出,即识别内容
+            data.put("items", items);       // 按 "||" 协议解析出的结构化行(可能为多条)
+            return R.ok(data, "识别成功");
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            return R.fail("OCR 执行被中断:" + e.getMessage());
         } catch (Exception e) {
-            return R.fail(e.getMessage());
+            e.printStackTrace();
+            return R.fail("OCR 执行异常:" + e.getMessage());
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException ignored) {
+                    // 关闭失败无需处理
+                }
+            }
+            if (process != null && process.isAlive()) {
+                process.destroy();
+            }
+        }
+    }
+
+    /**
+     * 截断过长的脚本输出,避免异常信息刷爆日志/响应
+     */
+    private String truncate(String text) {
+        if (StringUtils.isBlank(text)) {
+            return "";
         }
+        String trim = text.trim();
+        return trim.length() <= MAX_ERROR_LENGTH ? trim : trim.substring(0, MAX_ERROR_LENGTH) + "...";
     }