From f2e11f8f5077e666818bcd7bb1e0cfd945c686c8 Mon Sep 17 00:00:00 2001 From: qianbao Date: Wed, 1 Apr 2026 11:10:25 +0800 Subject: [PATCH] 添加向量库 --- src/main/java/com/xly/milvus/service/AiGlobalAgentQuestionSqlEmitterService.java | 2 +- src/main/java/com/xly/milvus/service/MilvusService.java | 18 ++++++++++++++++++ src/main/java/com/xly/milvus/service/impl/AiGlobalAgentQuestionSqlEmitterServiceImpl.java | 12 +++++++----- src/main/java/com/xly/milvus/service/impl/MilvusServiceImpl.java | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/xly/milvus/web/MilvusController.java | 26 ++++++++++++++++++++++++++ src/main/java/com/xly/ocr/service/OcrService.java | 33 ++++++++++----------------------- src/main/java/com/xly/ocr/test/Test.java | 182 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/main/java/com/xly/ocr/util/OcrUtil.java | 259 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/xly/ocr/web/OcrController.java | 79 +++++++++++++++++++++++++++++++++++++------------------------------------------ src/main/java/com/xly/service/XlyErpService.java | 8 +++----- src/main/java/com/xly/thread/AiUserAgentQuestionThread.java | 2 +- src/main/resources/application.yml | 2 ++ 12 files changed, 448 insertions(+), 259 deletions(-) delete mode 100644 src/main/java/com/xly/ocr/test/Test.java create mode 100644 src/main/java/com/xly/ocr/util/OcrUtil.java diff --git a/src/main/java/com/xly/milvus/service/AiGlobalAgentQuestionSqlEmitterService.java b/src/main/java/com/xly/milvus/service/AiGlobalAgentQuestionSqlEmitterService.java index c560544..29abd7e 100644 --- a/src/main/java/com/xly/milvus/service/AiGlobalAgentQuestionSqlEmitterService.java +++ b/src/main/java/com/xly/milvus/service/AiGlobalAgentQuestionSqlEmitterService.java @@ -12,7 +12,7 @@ public interface AiGlobalAgentQuestionSqlEmitterService { * @return void * @Description 插入向量库 **/ - void addAiGlobalAgentQuestionSqlEmitter(String sKey,Map data,String sQuestion,String sSqlContent,String cachType,String collectionName); + void addAiGlobalAgentQuestionSqlEmitter(String sKey,Map data,String sQuestion,String sSqlContent,String cachType,String collectionName,Boolean isInit); Map queryAiGlobalAgentQuestionSqlEmitter(String searchText, String collectionName); diff --git a/src/main/java/com/xly/milvus/service/MilvusService.java b/src/main/java/com/xly/milvus/service/MilvusService.java index 1ecee9e..1bb4e89 100644 --- a/src/main/java/com/xly/milvus/service/MilvusService.java +++ b/src/main/java/com/xly/milvus/service/MilvusService.java @@ -22,6 +22,24 @@ public interface MilvusService { TTSResponseDTO initDataToMilvus(Map reqMap); + /*** + * @Author 钱豹 + * @Date 9:00 2026/4/1 + * @Param [reqMap] + * @return com.xly.tts.bean.TTSResponseDTO + * @Description 客户问题库转入全局问题库 + **/ + TTSResponseDTO addGlobalAgentQuestion(Map reqMap); + + /*** + * @Author 钱豹 + * @Date 9:28 2026/4/1 + * @Param [reqMap] + * @return com.xly.tts.bean.TTSResponseDTO + * @Description 初始化全局SQL进入向量库 + **/ + TTSResponseDTO initGlobalAgentQuestion(Map reqMap); + /** * 创建集合(如果不存在) diff --git a/src/main/java/com/xly/milvus/service/impl/AiGlobalAgentQuestionSqlEmitterServiceImpl.java b/src/main/java/com/xly/milvus/service/impl/AiGlobalAgentQuestionSqlEmitterServiceImpl.java index f86cd02..5104e58 100644 --- a/src/main/java/com/xly/milvus/service/impl/AiGlobalAgentQuestionSqlEmitterServiceImpl.java +++ b/src/main/java/com/xly/milvus/service/impl/AiGlobalAgentQuestionSqlEmitterServiceImpl.java @@ -58,7 +58,7 @@ public class AiGlobalAgentQuestionSqlEmitterServiceImpl implements AiGlobalAgent * @Description 插入数据 **/ @Override - public void addAiGlobalAgentQuestionSqlEmitter(String sKey,Map data,String sQuestion,String sSqlContent,String cachType,String collectionName) { + public void addAiGlobalAgentQuestionSqlEmitter(String sKey,Map data,String sQuestion,String sSqlContent,String cachType,String collectionName,Boolean isInit) { // 向量化 List vector = vectorizationService.textToVector(sKey); @@ -83,10 +83,12 @@ public class AiGlobalAgentQuestionSqlEmitterServiceImpl implements AiGlobalAgent .build(); InsertResp insertResp = milvusClient.insert(insertReq); - //调用数据库插入数据库 - Map searMap = dynamicExeDbService.getDoProMap(sProName, data); - dynamicExeDbService.getCallPro(searMap, sProName); - + //是否初始化。初始化不需要插入到数据库 + if(!isInit){ + //调用数据库插入数据库 + Map searMap = dynamicExeDbService.getDoProMap(sProName, data); + dynamicExeDbService.getCallPro(searMap, sProName); + } System.out.println("成功插入 " + insertResp.getInsertCnt() + " 条数据"); System.out.println(" - 数据预览:"); } diff --git a/src/main/java/com/xly/milvus/service/impl/MilvusServiceImpl.java b/src/main/java/com/xly/milvus/service/impl/MilvusServiceImpl.java index 5c8b892..b4d396e 100644 --- a/src/main/java/com/xly/milvus/service/impl/MilvusServiceImpl.java +++ b/src/main/java/com/xly/milvus/service/impl/MilvusServiceImpl.java @@ -10,6 +10,7 @@ import com.google.common.reflect.TypeToken; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import com.xly.milvus.service.AiGlobalAgentQuestionSqlEmitterService; import com.xly.milvus.service.MilvusService; import com.xly.milvus.service.VectorizationService; import com.xly.milvus.util.MapToJsonConverter; @@ -60,6 +61,7 @@ public class MilvusServiceImpl implements MilvusService { private final MilvusClientV2 milvusClient; private final VectorizationService vectorizationService; private final DynamicExeDbService dynamicExeDbService; + private final AiGlobalAgentQuestionSqlEmitterService aiGlobalAgentQuestionSqlEmitterService; private static final long NULL_TIMESTAMP = -1L; // 日期格式常量 private static final DateTimeFormatter ISO_FORMATTER = @@ -128,6 +130,80 @@ public class MilvusServiceImpl implements MilvusService { .build(); } + /*** + * @Author 钱豹 + * @Date 9:20 2026/4/1 + * @Param [reqMap] + * @return com.xly.tts.bean.TTSResponseDTO + * @Description 用户问题列表转换成全局问题 + **/ + @Override + public TTSResponseDTO addGlobalAgentQuestion(Map reqMap) { + if(ObjectUtil.isEmpty(reqMap.get("sRowData"))){ + return TTSResponseDTO.builder() + .code(-1) + .message("请选择数据") + .build(); + } + //遍历数据 + List> data = (List>) reqMap.get("sRowData"); + if(ObjectUtil.isEmpty(data)){ + return TTSResponseDTO.builder() + .code(-1) + .message("请选择数据") + .build(); + } + StringBuffer sb = new StringBuffer(); + data.forEach(one->{ + //查询向量库是否存在 + String sSceneId = one.get("sSceneId").toString(); + String sMethodId = one.get("sMethodId").toString(); + String sUserInput = one.get("sUserInput").toString(); + String sSqlContent = one.get("sSqlContent").toString(); + String searchText = sSceneId+"_"+sMethodId+"_"+sUserInput; + if(ObjectUtil.isNotEmpty(sUserInput)){ + //根据问题查询向量库 + Map serMap = aiGlobalAgentQuestionSqlEmitterService.queryAiGlobalAgentQuestionSqlEmitter(searchText, "ai_global_agent_question_sql"); + if(ObjectUtil.isNotEmpty(serMap)){ + aiGlobalAgentQuestionSqlEmitterService.addAiGlobalAgentQuestionSqlEmitter(searchText,one,sUserInput,sSqlContent,"MYSQL","ai_global_agent_question_sql",false); + }else{ + sb.append(sUserInput).append("已经存在").append("\n"); + } + //更新数据是否是全局问题,等待朱总确定是否需要 + + + } + }); + if(ObjectUtil.isEmpty(sb)){ + sb.append("操作成功"); + } + return TTSResponseDTO.builder() + .code(200) + .message(sb.toString()) + .build(); + } + + @Override + public TTSResponseDTO initGlobalAgentQuestion(Map reqMap) { + //全局问题初始化 + List> data = getGlobalAgentQuestion(); + data.forEach(one->{ + //查询向量库是否存在 + String sSceneId = one.get("sSceneId").toString(); + String sMethodId = one.get("sMethodId").toString(); + String sUserInput = one.get("sUserInput").toString(); + String sSqlContent = one.get("sSqlContent").toString(); + String searchText = sSceneId+"_"+sMethodId+"_"+sUserInput; + if(ObjectUtil.isNotEmpty(sUserInput)){ + aiGlobalAgentQuestionSqlEmitterService.addAiGlobalAgentQuestionSqlEmitter(searchText,one,sUserInput,sSqlContent,"MYSQL","ai_global_agent_question_sql",true); + } + }); + return TTSResponseDTO.builder() + .code(200) + .message("操作成功") + .build(); + } + public String getUpdateDateUp(String sInputTabelName) { Map serDataMap = new HashMap<>(); String sSql ="SELECT DATE_FORMAT(tUpdateDate,'%Y-%m-%d %H:%i:%s') AS tUpdateDate FROM ai_milvus_vector_record WHERE sInputTabelName = #{sInputTabelName}"; @@ -139,6 +215,14 @@ public class MilvusServiceImpl implements MilvusService { return data.get(0).get("tUpdateDate").toString(); } + public List> getGlobalAgentQuestion() { + Map serDataMap = new HashMap<>(); + String sSql ="SELECT * FROM ai_global_agent_question_sql"; + //删除集合全部数据 + List> data = this.dynamicExeDbService.findSql(serDataMap,sSql); + return data; + } + /*** * @Author 钱豹 * @Date 22:24 2026/3/24 diff --git a/src/main/java/com/xly/milvus/web/MilvusController.java b/src/main/java/com/xly/milvus/web/MilvusController.java index 80ff82e..b9000f5 100644 --- a/src/main/java/com/xly/milvus/web/MilvusController.java +++ b/src/main/java/com/xly/milvus/web/MilvusController.java @@ -45,5 +45,31 @@ public class MilvusController { return ResponseEntity.ok(responseDTO); } + /*** + * @Author 钱豹 + * @Date 14:32 2026/2/10 + * @Param [request] + * @return org.springframework.http.ResponseEntity + * @Description 初始化AI所有变量 热启动 + **/ + @PostMapping("/addGlobalAgentQuestion") + public ResponseEntity addGlobalAgentQuestion(@RequestBody Map reqMap) { + TTSResponseDTO responseDTO = milvusService.addGlobalAgentQuestion(reqMap); + return ResponseEntity.ok(responseDTO); + } + + /*** + * @Author 钱豹 + * @Date 14:32 2026/2/10 + * @Param [request] + * @return org.springframework.http.ResponseEntity + * @Description 初始化全局SQL + **/ + @PostMapping("/initGlobalAgentQuestion") + public ResponseEntity initGlobalAgentQuestion(@RequestBody Map reqMap) { + TTSResponseDTO responseDTO = milvusService.initGlobalAgentQuestion(reqMap); + return ResponseEntity.ok(responseDTO); + } + } \ No newline at end of file diff --git a/src/main/java/com/xly/ocr/service/OcrService.java b/src/main/java/com/xly/ocr/service/OcrService.java index 5641e4e..80a00bd 100644 --- a/src/main/java/com/xly/ocr/service/OcrService.java +++ b/src/main/java/com/xly/ocr/service/OcrService.java @@ -1,5 +1,7 @@ package com.xly.ocr.service; +import com.xly.ocr.util.OcrUtil; +import lombok.extern.slf4j.Slf4j; import net.sourceforge.tess4j.Tesseract; import net.sourceforge.tess4j.TesseractException; import org.slf4j.Logger; @@ -18,11 +20,16 @@ import java.nio.file.Path; import java.util.Arrays; import java.util.List; -@Service +@Slf4j +@Service("ocrService") public class OcrService { private static final Logger logger = LoggerFactory.getLogger(OcrService.class); + @Value("${ocr.tmpPath}") + private String tmpPath; + + private final Tesseract tesseract; // 配置参数 @@ -457,28 +464,8 @@ public class OcrService { logger.warn("不支持的文件格式: {}", originalFilename); return "不支持的文件格式,仅支持: " + String.join(", ", ALLOWED_EXTENSIONS); } - - Path tempFile = null; - try { - // 创建临时文件 - String suffix = getFileExtension(originalFilename); - tempFile = Files.createTempFile("ocr_", suffix); - file.transferTo(tempFile.toFile()); - - logger.info("临时文件创建成功: {}", tempFile); - - // 执行 OCR - String result = extractText(tempFile.toFile()); - - return result; - - } catch (IOException e) { - logger.error("文件处理失败: {}", e.getMessage(), e); - return "文件处理失败: " + e.getMessage(); - } finally { - // 清理临时文件 - cleanupTempFile(tempFile); - } + String sText = OcrUtil.ocrFile(file,tmpPath); + return sText; } /** diff --git a/src/main/java/com/xly/ocr/test/Test.java b/src/main/java/com/xly/ocr/test/Test.java deleted file mode 100644 index c08789e..0000000 --- a/src/main/java/com/xly/ocr/test/Test.java +++ /dev/null @@ -1,182 +0,0 @@ -package com.xly.ocr.test; - -import com.benjaminwan.ocrlibrary.OcrResult; -import com.benjaminwan.ocrlibrary.TextBlock; -import io.github.mymonstercat.Model; -import io.github.mymonstercat.ocr.InferenceEngine; -import io.github.mymonstercat.ocr.config.ParamConfig; - -import javax.imageio.ImageIO; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.util.List; - -public class Test { - - static { - try { - String customTempDir = "D:/temp/ocrJava"; - File tempDir = new File(customTempDir); - if (!tempDir.exists()) { - tempDir.mkdirs(); - } - System.setProperty("java.io.tmpdir", customTempDir); - System.setProperty("TMP", customTempDir); - System.setProperty("TEMP", customTempDir); - - System.out.println("=================================="); - System.out.println("临时目录: " + System.getProperty("java.io.tmpdir")); - System.out.println("=================================="); - - } catch (Exception e) { - System.err.println("设置临时目录失败: " + e.getMessage()); - } - } - - public static void main(String[] args) { - try { - System.out.println("OCR 程序开始执行..."); - - // 1. 初始化引擎(使用 v4 模型) - System.out.println("正在初始化 OCR 引擎 (PP-OCRv4)..."); - InferenceEngine engine = InferenceEngine.getInstance(Model.ONNX_PPOCR_V4); - - // 2. 创建优化的参数配置 - ParamConfig config = createOptimizedParamConfig(); - - // 3. 图片路径 - String imagePath = "E:/aa/b.jpg"; - File imageFile = new File(imagePath); - if (!imageFile.exists()) { - System.err.println("图片文件不存在: " + imagePath); - return; - } - - // 4. 图像预处理(直接处理原图,不保存临时文件) - System.out.println("正在进行图像预处理..."); - BufferedImage processedImage = preprocessImage(imageFile); - - // 5. 保存预处理后的图片到临时目录 - String processedImagePath = "D:/temp/ocrJava/processed_" + System.currentTimeMillis() + ".png"; - ImageIO.write(processedImage, "png", new File(processedImagePath)); - System.out.println("预处理图片已保存: " + processedImagePath); - - // 6. 执行识别 - System.out.println("开始识别图片..."); - long startTime = System.currentTimeMillis(); - OcrResult ocrResult = engine.runOcr(processedImagePath, config); - long endTime = System.currentTimeMillis(); - - // 7. 输出结果 - String text = ocrResult.getStrRes().trim(); - System.out.println("\n=================================="); - System.out.println("识别结果:"); - System.out.println(text); - System.out.println("=================================="); - System.out.println("识别耗时: " + (endTime - startTime) + " ms"); - - // 8. 输出每个文本块 - if (ocrResult.getTextBlocks() != null && !ocrResult.getTextBlocks().isEmpty()) { - System.out.println("\n文本块详情(共" + ocrResult.getTextBlocks().size() + "块):"); - List textBlocks = ocrResult.getTextBlocks(); - for (int i = 0; i < textBlocks.size(); i++) { - TextBlock block = textBlocks.get(i); - System.out.printf(" 块%d: %s (置信度: %.2f)%n", - i + 1, - block.getText(), - block.getBoxScore() - ); - } - } - - // 9. 清理临时文件 - new File(processedImagePath).delete(); - - } catch (Exception e) { - System.err.println("OCR 识别失败: " + e.getMessage()); - e.printStackTrace(); - } - } - - /** - * 优化的参数配置 - */ - private static ParamConfig createOptimizedParamConfig() { - ParamConfig config = new ParamConfig(); - - config.setPadding(50); - config.setMaxSideLen(0); - config.setBoxScoreThresh(0.4f); - config.setBoxThresh(0.25f); - config.setUnClipRatio(1.8f); - config.setDoAngle(true); - config.setMostAngle(true); - - return config; - } - - /** - * 图像预处理 - 直接返回处理后的 BufferedImage - */ - private static BufferedImage preprocessImage(File imageFile) throws IOException { - BufferedImage original = ImageIO.read(imageFile); - if (original == null) { - throw new IOException("无法读取图片: " + imageFile.getPath()); - } - - System.out.println("原始尺寸: " + original.getWidth() + "x" + original.getHeight()); - - BufferedImage processed = original; - - // 1. 如果图片太大,缩小尺寸 - if (processed.getWidth() > 2000 || processed.getHeight() > 2000) { - processed = resizeImage(processed, 1600, 1600); - } - - // 2. 增强对比度 - processed = enhanceContrast(processed); - - System.out.println("处理后尺寸: " + processed.getWidth() + "x" + processed.getHeight()); - - return processed; - } - - /** - * 调整图片大小 - */ - private static BufferedImage resizeImage(BufferedImage image, int maxWidth, int maxHeight) { - int w = image.getWidth(); - int h = image.getHeight(); - double ratio = Math.min((double) maxWidth / w, (double) maxHeight / h); - if (ratio >= 1) return image; - - int newW = (int) (w * ratio); - int newH = (int) (h * ratio); - - BufferedImage resized = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_RGB); - Graphics2D g = resized.createGraphics(); - g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); - g.drawImage(image, 0, 0, newW, newH, null); - g.dispose(); - return resized; - } - - /** - * 增强对比度 - */ - private static BufferedImage enhanceContrast(BufferedImage image) { - BufferedImage result = new BufferedImage(image.getWidth(), image.getHeight(), image.getType()); - for (int y = 0; y < image.getHeight(); y++) { - for (int x = 0; x < image.getWidth(); x++) { - Color c = new Color(image.getRGB(x, y)); - int r = Math.min(255, (int) (c.getRed() * 1.15)); - int g = Math.min(255, (int) (c.getGreen() * 1.15)); - int b = Math.min(255, (int) (c.getBlue() * 1.15)); - result.setRGB(x, y, new Color(r, g, b).getRGB()); - } - } - return result; - } -} \ No newline at end of file diff --git a/src/main/java/com/xly/ocr/util/OcrUtil.java b/src/main/java/com/xly/ocr/util/OcrUtil.java new file mode 100644 index 0000000..fb231a6 --- /dev/null +++ b/src/main/java/com/xly/ocr/util/OcrUtil.java @@ -0,0 +1,259 @@ +package com.xly.ocr.util; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.StrUtil; +import com.benjaminwan.ocrlibrary.OcrResult; +import com.benjaminwan.ocrlibrary.TextBlock; +import io.github.mymonstercat.Model; +import io.github.mymonstercat.ocr.InferenceEngine; +import io.github.mymonstercat.ocr.config.ParamConfig; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.multipart.MultipartFile; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.List; + +@Slf4j +public class OcrUtil { + + static { + try { + String customTempDir = "D:/temp/ocrJava"; + File tempDir = new File(customTempDir); + if (!tempDir.exists()) { + tempDir.mkdirs(); + } + System.setProperty("java.io.tmpdir", customTempDir); + System.setProperty("TMP", customTempDir); + System.setProperty("TEMP", customTempDir); + + System.out.println("=================================="); + System.out.println("临时目录: " + System.getProperty("java.io.tmpdir")); + System.out.println("=================================="); + + } catch (Exception e) { + System.err.println("设置临时目录失败: " + e.getMessage()); + } + } + + public static String ocrFile(MultipartFile imageFile, String sTmpPath){ + String processedImagePath = StrUtil.EMPTY; + try { + log.info("OCR 程序开始执行..."); + // 1. 初始化引擎(使用 v4 模型) + log.info("正在初始化 OCR 引擎 (PP-OCRv4)..."); + InferenceEngine engine = InferenceEngine.getInstance(Model.ONNX_PPOCR_V4); + // 2. 创建优化的参数配置 + ParamConfig config = createOptimizedParamConfig(); + // 4. 图像预处理(直接处理原图,不保存临时文件) + System.out.println("正在进行图像预处理..."); + File file = multipartFileToFile(imageFile); + BufferedImage processedImage = preprocessImage(file); + // 5. 保存预处理后的图片到临时目录 + if(!FileUtil.exist(sTmpPath)){ + FileUtil.mkdir(sTmpPath); + } + processedImagePath = sTmpPath+"/processed_" + System.currentTimeMillis() + ".png"; + ImageIO.write(processedImage, "png", new File(processedImagePath)); + log.info("预处理图片已保存: " + processedImagePath); + // 6. 执行识别 + log.info("开始识别图片..."); + long startTime = System.currentTimeMillis(); + OcrResult ocrResult = engine.runOcr(processedImagePath, config); + long endTime = System.currentTimeMillis(); + // 7. 输出结果 + String text = ocrResult.getStrRes().trim(); + log.info("\n=================================="); + log.info("识别结果:"); + log.info(text); + log.info("=================================="); + log.info("识别耗时: " + (endTime - startTime) + " ms"); + // 8. 输出每个文本块 +// if (ocrResult.getTextBlocks() != null && !ocrResult.getTextBlocks().isEmpty()) { +// System.out.println("\n文本块详情(共" + ocrResult.getTextBlocks().size() + "块):"); +// List textBlocks = ocrResult.getTextBlocks(); +// for (int i = 0; i < textBlocks.size(); i++) { +// TextBlock block = textBlocks.get(i); +// System.out.printf(" 块%d: %s (置信度: %.2f)%n", +// i + 1, +// block.getText(), +// block.getBoxScore() +// ); +// } +// } + return text; + + } catch (Exception e) { + System.err.println("OCR 识别失败: " + e.getMessage()); + e.printStackTrace(); + }finally { + // 9. 清理临时文件 + FileUtil.del(processedImagePath); + } + return StrUtil.EMPTY; + } + + + /** + * 优化的参数配置 + */ + private static ParamConfig createOptimizedParamConfig() { + ParamConfig config = new ParamConfig(); + + config.setPadding(50); + config.setMaxSideLen(0); + config.setBoxScoreThresh(0.4f); + config.setBoxThresh(0.25f); + config.setUnClipRatio(1.8f); + config.setDoAngle(true); + config.setMostAngle(true); + + return config; + } + + /** + * 图像预处理 - 直接返回处理后的 BufferedImage + */ + private static BufferedImage preprocessImage(File imageFile) throws IOException { + BufferedImage original = ImageIO.read(imageFile); + if (original == null) { + throw new IOException("无法读取图片: " + imageFile.getPath()); + } + + System.out.println("原始尺寸: " + original.getWidth() + "x" + original.getHeight()); + + BufferedImage processed = original; + + // 1. 如果图片太大,缩小尺寸 + if (processed.getWidth() > 2000 || processed.getHeight() > 2000) { + processed = resizeImage(processed, 1600, 1600); + } + + // 2. 增强对比度 + processed = enhanceContrast(processed); + + System.out.println("处理后尺寸: " + processed.getWidth() + "x" + processed.getHeight()); + + return processed; + } + /*** + * @Author 钱豹 + * @Date 11:01 2026/4/1 + * @Param [multipartFile] + * @return java.io.File + * @Description 图片对象转换 + **/ + public static File multipartFileToFile(MultipartFile multipartFile) throws IOException { + // 创建临时文件 + File file = File.createTempFile("temp", null); + // 将 MultipartFile 的内容传输到 File + multipartFile.transferTo(file); + return file; + } + + /** + * 调整图片大小 + */ + private static BufferedImage resizeImage(BufferedImage image, int maxWidth, int maxHeight) { + int w = image.getWidth(); + int h = image.getHeight(); + double ratio = Math.min((double) maxWidth / w, (double) maxHeight / h); + if (ratio >= 1) return image; + + int newW = (int) (w * ratio); + int newH = (int) (h * ratio); + + BufferedImage resized = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_RGB); + Graphics2D g = resized.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g.drawImage(image, 0, 0, newW, newH, null); + g.dispose(); + return resized; + } + + /** + * 增强对比度 + */ + private static BufferedImage enhanceContrast(BufferedImage image) { + BufferedImage result = new BufferedImage(image.getWidth(), image.getHeight(), image.getType()); + for (int y = 0; y < image.getHeight(); y++) { + for (int x = 0; x < image.getWidth(); x++) { + Color c = new Color(image.getRGB(x, y)); + int r = Math.min(255, (int) (c.getRed() * 1.15)); + int g = Math.min(255, (int) (c.getGreen() * 1.15)); + int b = Math.min(255, (int) (c.getBlue() * 1.15)); + result.setRGB(x, y, new Color(r, g, b).getRGB()); + } + } + return result; + } + + public static void main(String[] args) { + try { + System.out.println("OCR 程序开始执行..."); + + // 1. 初始化引擎(使用 v4 模型) + System.out.println("正在初始化 OCR 引擎 (PP-OCRv4)..."); + InferenceEngine engine = InferenceEngine.getInstance(Model.ONNX_PPOCR_V4); + + // 2. 创建优化的参数配置 + ParamConfig config = createOptimizedParamConfig(); + + // 3. 图片路径 + String imagePath = "E:/aa/b.jpg"; + File imageFile = new File(imagePath); + if (!imageFile.exists()) { + System.err.println("图片文件不存在: " + imagePath); + return; + } + + // 4. 图像预处理(直接处理原图,不保存临时文件) + System.out.println("正在进行图像预处理..."); + BufferedImage processedImage = preprocessImage(imageFile); + + // 5. 保存预处理后的图片到临时目录 + String processedImagePath = "D:/temp/ocrJava/processed_" + System.currentTimeMillis() + ".png"; + ImageIO.write(processedImage, "png", new File(processedImagePath)); + System.out.println("预处理图片已保存: " + processedImagePath); + + // 6. 执行识别 + System.out.println("开始识别图片..."); + long startTime = System.currentTimeMillis(); + OcrResult ocrResult = engine.runOcr(processedImagePath, config); + long endTime = System.currentTimeMillis(); + + // 7. 输出结果 + String text = ocrResult.getStrRes().trim(); + System.out.println("\n=================================="); + System.out.println("识别结果:"); + System.out.println(text); + System.out.println("=================================="); + System.out.println("识别耗时: " + (endTime - startTime) + " ms"); + + // 8. 输出每个文本块 + if (ocrResult.getTextBlocks() != null && !ocrResult.getTextBlocks().isEmpty()) { + System.out.println("\n文本块详情(共" + ocrResult.getTextBlocks().size() + "块):"); + List textBlocks = ocrResult.getTextBlocks(); + for (int i = 0; i < textBlocks.size(); i++) { + TextBlock block = textBlocks.get(i); + System.out.printf(" 块%d: %s (置信度: %.2f)%n", + i + 1, + block.getText(), + block.getBoxScore() + ); + } + } + + // 9. 清理临时文件 + new File(processedImagePath).delete(); + + } catch (Exception e) { + System.err.println("OCR 识别失败: " + e.getMessage()); + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/xly/ocr/web/OcrController.java b/src/main/java/com/xly/ocr/web/OcrController.java index e56a99f..f5c1117 100644 --- a/src/main/java/com/xly/ocr/web/OcrController.java +++ b/src/main/java/com/xly/ocr/web/OcrController.java @@ -1,42 +1,37 @@ -//package com.xly.ocr.web; -// -//import com.xly.ocr.service.OcrService; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.http.ResponseEntity; -//import org.springframework.web.bind.annotation.*; -//import org.springframework.web.multipart.MultipartFile; -// -//import java.util.HashMap; -//import java.util.List; -//import java.util.Map; -// -//@RestController -//@RequestMapping("/api/ocr") -//public class OcrController { -// -// @Autowired -// private OcrService ocrService; -// -// @PostMapping("/extract") -// public ResponseEntity> extractText( -// @RequestParam("file") MultipartFile file) { -// -// Map response = new HashMap<>(); -// long startTime = System.currentTimeMillis(); -// -// String result = ocrService.extractTextFromMultipartFile(file); -// -// response.put("text", result); -// response.put("time", System.currentTimeMillis() - startTime); -// response.put("success", !result.startsWith("错误") && !result.startsWith("失败")); -// -// return ResponseEntity.ok(response); -// } -// -// @PostMapping("/batch") -// public ResponseEntity> batchExtract( -// @RequestParam("files") List files) { -// List results = ocrService.batchExtractText(files); -// return ResponseEntity.ok(results); -// } -//} \ No newline at end of file +package com.xly.ocr.web; + +import com.xly.ocr.service.OcrService; +import com.xly.tts.bean.TTSResponseDTO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +@RestController +@RequestMapping("/api/ocr") +public class OcrController { + + @Autowired + private OcrService ocrService; + + @PostMapping("/extract") + public ResponseEntity extractText( + @RequestParam("file") MultipartFile file) { + String result = ocrService.extractTextFromMultipartFile(file); + TTSResponseDTO dto= TTSResponseDTO.builder() + .code(200) + .message("操作成功") + .processedText(result) + .build(); + return ResponseEntity.ok(dto); + } + + @PostMapping("/batch") + public ResponseEntity> batchExtract( + @RequestParam("files") List files) { + List results = ocrService.batchExtractText(files); + return ResponseEntity.ok(results); + } +} \ No newline at end of file diff --git a/src/main/java/com/xly/service/XlyErpService.java b/src/main/java/com/xly/service/XlyErpService.java index 4a9af33..2c84bcb 100644 --- a/src/main/java/com/xly/service/XlyErpService.java +++ b/src/main/java/com/xly/service/XlyErpService.java @@ -668,10 +668,8 @@ public class XlyErpService { //如果查询不到数据走向量库 if(ObjectUtil.isEmpty(sqlResult)){ session.setDbType("X"); - //数据集为空的也记录到历史问题中 - doAiSqlErrorHistoryThread(session, cleanSql, StrUtil.EMPTY, StrUtil.EMPTY,input); - + doAiSqlErrorHistoryThread(session, StrUtil.EMPTY,cleanSql, "结果为空",input); return getMilvus(session, input, aiAgent,false); } // 5. 调用AI服务生成自然语言解释(传入表结构,让解释更贴合业务) @@ -679,7 +677,7 @@ public class XlyErpService { //执行正确去修改对应正确的SQl if(Integer.valueOf(iErroCount)>0){ - doAiSqlErrorHistoryThread(session, cleanSql, StrUtil.EMPTY, StrUtil.EMPTY,input); + doAiSqlErrorHistoryThread(session, cleanSql, StrUtil.EMPTY, "执行正确去修改对应正确的SQl",input); } //插入常用操作 不包含where 条件 if(doAddSql){ @@ -733,7 +731,7 @@ public class XlyErpService { **/ private Map getDynamicTableCach(UserSceneSession session,String input){ try{ - String searchText = session.getCurrentScene().getSId()+"_"+session.getCurrentTool().getSId()+input; + String searchText = session.getCurrentScene().getSId()+"_"+session.getCurrentTool().getSId()+"_"+input; //根据问题查询向量库 Map serMap = aiGlobalAgentQuestionSqlEmitterService.queryAiGlobalAgentQuestionSqlEmitter(searchText, "ai_global_agent_question_sql"); return serMap; diff --git a/src/main/java/com/xly/thread/AiUserAgentQuestionThread.java b/src/main/java/com/xly/thread/AiUserAgentQuestionThread.java index c9374ac..40ea970 100644 --- a/src/main/java/com/xly/thread/AiUserAgentQuestionThread.java +++ b/src/main/java/com/xly/thread/AiUserAgentQuestionThread.java @@ -57,7 +57,7 @@ public class AiUserAgentQuestionThread implements Runnable { // SqlValidateUtil.getsKey( sSceneId, sMethodId, SqlValidateUtil.getsQuestion(session.getSUserQuestionList())); //存入向量库 不包含where 条件 if(!SqlWhereHelper.hasWhereButNoCompareOperators(sSqlContent)){ - aiGlobalAgentQuestionSqlEmitterService.addAiGlobalAgentQuestionSqlEmitter(sKey,data,sQuestion,sSqlContent,cachType,"ai_global_agent_question_sql"); + aiGlobalAgentQuestionSqlEmitterService.addAiGlobalAgentQuestionSqlEmitter(sKey,data,sQuestion,sSqlContent,cachType,"ai_global_agent_question_sql",false); } //调用数据库插入数据库 Map searMap = dynamicExeDbService.getDoProMap(sProName, data); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0132d77..abcd1c5 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -65,6 +65,8 @@ spring: max-active: 8 # 连接池最大连接数 max-idle: 8 # 连接池最大空闲连接 min-idle: 0 # 连接池最小空闲连接' +ocr: + tmpPath: D:/xlyweberp/ai/ocrtmp milvus: host: 112.82.245.194 -- libgit2 0.22.2