From 0ffdf00009b0bede0859fa33deddefb55c075a7b Mon Sep 17 00:00:00 2001
From: wlzboy <66905212@qq.com>
Date: 星期日, 01 二月 2026 16:42:36 +0800
Subject: [PATCH] feat:优化增加任务同步接口,允许前端手动控制同步

---
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/HospDataController.java |    2 
 ruoyi-common/src/main/java/com/ruoyi/common/utils/image/package-info.java         |   63 ++++
 ruoyi-system/src/main/resources/mapper/system/SysTaskMapper.xml                   |   29 +
 ruoyi-system/src/main/java/com/ruoyi/system/controller/OCRController.java         |   35 +-
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskController.java    |   89 +++++
 ruoyi-ui/src/views/task/general/detail.vue                                        |   57 +++
 app/pagesTask/create-emergency.vue                                                |   96 ++++--
 ruoyi-common/src/main/java/com/ruoyi/common/utils/HospitalTokenizerUtil.java      |   17 
 ruoyi-common/src/main/java/com/ruoyi/common/utils/image/ImageCompressUtil.java    |  374 ++++++++++++++++++++++++
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserSyncServiceImpl.java |   16 
 ruoyi-system/src/main/java/com/ruoyi/system/utils/TencentOCRUtil.java             |   79 ++++
 ruoyi-ui/src/api/task.js                                                          |   18 +
 12 files changed, 802 insertions(+), 73 deletions(-)

diff --git a/app/pagesTask/create-emergency.vue b/app/pagesTask/create-emergency.vue
index e68decb..d1adac9 100644
--- a/app/pagesTask/create-emergency.vue
+++ b/app/pagesTask/create-emergency.vue
@@ -2006,6 +2006,14 @@
       // - YYYYMMDD
       // - yyyy-MM-dd HH:mm:ss
       console.log('灏濊瘯鏍煎紡鍖栨棩鏈熷瓧绗︿覆:', dateStr)
+      
+      // 濡傛灉杈撳叆涓虹┖鎴栨棤鏁堬紝杩斿洖绌哄瓧绗︿覆
+      if (!dateStr || typeof dateStr !== 'string') {
+        console.warn('鏃ユ湡瀛楃涓叉棤鏁�:', dateStr)
+        return ''
+      }
+      
+      // 娓呮礂鏃ユ湡瀛楃涓�
       let cleaned = dateStr
         .replace(/[骞存湀]/g, '-')
         .replace(/[鏃ュ彿]/g, ' ')  // 鏃�/鍙� 鈫� 绌烘牸锛屼繚鐣欐棩鏈熷拰鏃堕棿鐨勫垎闅�
@@ -2014,50 +2022,76 @@
         .replace(/绉�/g, '')
         .replace(/\s+/g, ' ')  // 澶氫釜绌烘牸鍚堝苟涓轰竴涓�
         .trim()
+      
       console.log('娓呯悊鍚庣殑鏃ユ湡瀛楃涓�:', cleaned)
+      
+      // 鍒嗙鏃ユ湡鍜屾椂闂撮儴鍒�
+      const parts = cleaned.split(' ')
+      let datePart = parts[0] || ''
+      let timePart = parts[1] || ''
+      
       let dateResult = ''
       
+      // 澶勭悊鏃ユ湡閮ㄥ垎
       // 濡傛灉鏄痀YMMDD鏍煎紡
-      if (/^\d{6}$/.test(cleaned)) {
-        const year = '20' + cleaned.substring(0, 2)
-        const month = cleaned.substring(2, 4)
-        const day = cleaned.substring(4, 6)
-        dateResult = `${year}-${month}-${day}`;
-      }
-      // 濡傛灉鏄痀YYYMMDD鏍煎紡
-      else if (/^\d{8}$/.test(cleaned)) {
-        const year = cleaned.substring(0, 4)
-        const month = cleaned.substring(4, 6)
-        const day = cleaned.substring(6, 8)
+      if (/^\d{6}$/.test(datePart)) {
+        const year = '20' + datePart.substring(0, 2)
+        const month = datePart.substring(2, 4)
+        const day = datePart.substring(4, 6)
         dateResult = `${year}-${month}-${day}`
       }
-      // 濡傛灉宸茬粡鏄悎鐞嗘牸寮忥紝鐩存帴浣跨敤
-      else if (cleaned.match(/^\d{4}[-/]\d{1,2}[-/]\d{1,2}$/)) {
-        dateResult = cleaned.replace(/[//]/g, '-')
+      // 濡傛灉鏄痀YYYMMDD鏍煎紡
+      else if (/^\d{8}$/.test(datePart)) {
+        const year = datePart.substring(0, 4)
+        const month = datePart.substring(4, 6)
+        const day = datePart.substring(6, 8)
+        dateResult = `${year}-${month}-${day}`
       }
-      // 濡傛灉宸茬粡鍖呭惈鏃跺垎绉掞紝鐩存帴杩斿洖
-      else if (cleaned.match(/^\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2}$/)) {
-        return cleaned.replace(/[//]/g, '-')
-      }
-      // 濡傛灉鍖呭惈鏃跺垎浣嗙己灏戠锛坹yyy-MM-dd HH:mm:锛�
-      else if (cleaned.match(/^\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}:$/)) {
-        // 鍘绘帀鏈熬鐨勫啋鍙凤紝琛ヤ笂绉掓暟00
-        return cleaned.replace(/[//]/g, '-').replace(/:$/, '') + ':00'
-      }
-      // 濡傛灉鍙寘鍚椂鍒嗭紙yyyy-MM-dd HH:mm锛�
-      else if (cleaned.match(/^\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}$/)) {
-        return cleaned.replace(/[//]/g, '-') + ':00'
+      // 濡傛灉鏄痽yyy-MM-dd鎴杫yyy/MM/dd鏍煎紡
+      else if (datePart.match(/^\d{4}[-\/]\d{1,2}[-\/]\d{1,2}$/)) {
+        dateResult = datePart.replace(/\//g, '-')
       }
       else {
-        dateResult = dateStr
+        dateResult = datePart
       }
       
-      // 濡傛灉鏃ユ湡鏍煎紡姝g‘锛屾坊鍔犻粯璁ゆ椂鍒嗙 00:00:00
-      if (dateResult && dateResult.match(/^\d{4}-\d{1,2}-\d{1,2}$/)) {
-        return dateResult + ' 00:00:00'
+      // 楠岃瘉鏃ユ湡閮ㄥ垎鏄惁鏈夋晥
+      if (!dateResult.match(/^\d{4}-\d{1,2}-\d{1,2}$/)) {
+        console.warn('鏃ユ湡鏍煎紡涓嶆纭�:', dateResult)
+        return ''
       }
       
-      return dateResult
+      // 澶勭悊鏃堕棿閮ㄥ垎
+      let timeResult = '00:00:00'  // 榛樿鏃堕棿
+      
+      if (timePart) {
+        // 绉婚櫎鏈熬澶氫綑鐨勫啋鍙�
+        timePart = timePart.replace(/:+$/, '')
+        
+        // 鍒嗗壊鏃躲�佸垎銆佺
+        const timeParts = timePart.split(':')
+        const hour = timeParts[0] || '00'
+        const minute = timeParts[1] || '00'
+        const second = timeParts[2] || '00'
+        
+        // 楠岃瘉鏃堕棿鏁板瓧鏄惁鏈夋晥
+        const hourNum = parseInt(hour, 10)
+        const minuteNum = parseInt(minute, 10)
+        const secondNum = parseInt(second, 10)
+        
+        if (!isNaN(hourNum) && !isNaN(minuteNum) && !isNaN(secondNum) &&
+            hourNum >= 0 && hourNum < 24 && minuteNum >= 0 && minuteNum < 60 && secondNum >= 0 && secondNum < 60) {
+          // 琛ラ綈涓や綅鏁�
+          timeResult = `${String(hourNum).padStart(2, '0')}:${String(minuteNum).padStart(2, '0')}:${String(secondNum).padStart(2, '0')}`
+        } else {
+          console.warn('鏃堕棿鏁板�艰秴鍑鸿寖鍥达紝浣跨敤榛樿鍊�00:00:00')
+        }
+      }
+      
+      const finalResult = `${dateResult} ${timeResult}`
+      console.log('鏈�缁堟牸寮忓寲缁撴灉:', finalResult)
+      
+      return finalResult
     }
   }
 }
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/HospDataController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/HospDataController.java
index d17147c..5904fca 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/HospDataController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/HospDataController.java
@@ -39,7 +39,7 @@
     /**
      * 鍖婚櫌鎼滅储鏈�浣庡尮閰嶅垎鏁伴槇鍊硷紙浣庝簬姝ゅ垎鏁扮殑缁撴灉灏嗚杩囨护锛�
      */
-    private static final int MIN_MATCH_SCORE_THRESHOLD = 50;
+    private static final int MIN_MATCH_SCORE_THRESHOLD = 1;
     
     @Autowired
     private HospDataMapper hospDataMapper;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskController.java
index 56ad818..36a79da 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskController.java
@@ -11,6 +11,8 @@
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.system.domain.SysTaskEmergency;
 import com.ruoyi.system.service.*;
+import com.ruoyi.system.service.ILegacySystemSyncService;
+import com.ruoyi.system.service.ITaskDispatchSyncService;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -70,6 +72,12 @@
     @Autowired
     @Qualifier("tiandituMapService")
     private IMapService mapService;
+
+    @Autowired
+    private ILegacySystemSyncService legacySystemSyncService;
+    
+    @Autowired
+    private ITaskDispatchSyncService taskDispatchSyncService;
 
     /**
      * 鏌ヨ浠诲姟绠$悊鍒楄〃锛堝悗鍙扮鐞嗙锛�
@@ -615,4 +623,85 @@
             this.actualEndTime = actualEndTime;
         }
     }
+    
+    /**
+     * 鎵嬪姩鍚屾鏈嶅姟鍗曞埌鏃х郴缁�
+     * 褰撴湇鍔″崟鍚屾澶辫触鎴栨湭鍚屾鏃讹紝鍙互閫氳繃姝ゆ帴鍙f墜鍔ㄨЕ鍙戝悓姝�
+     */
+    @PreAuthorize("@ss.hasPermi('task:general:edit')")
+    @Log(title = "鎵嬪姩鍚屾鏈嶅姟鍗�", businessType = BusinessType.UPDATE)
+    @PostMapping("/syncServiceOrder/{taskId}")
+    public AjaxResult syncServiceOrder(@PathVariable Long taskId) {
+        try {
+            // 鏌ヨ浠诲姟淇℃伅
+            SysTask task = sysTaskService.selectSysTaskByTaskId(taskId);
+            if (task == null) {
+                return error("浠诲姟涓嶅瓨鍦�");
+            }
+            
+            // 鍙敮鎸佹�ユ晳杞繍浠诲姟
+            if (!"EMERGENCY_TRANSFER".equals(task.getTaskType())) {
+                return error("鍙湁鎬ユ晳杞繍浠诲姟鎵嶈兘鍚屾鍒版棫绯荤粺");
+            }
+            
+            // 璋冪敤鍚屾鏈嶅姟
+            Long serviceOrdId = legacySystemSyncService.syncEmergencyTaskToLegacy(taskId);
+            
+            if (serviceOrdId != null && serviceOrdId > 0) {
+                return success("鏈嶅姟鍗曞悓姝ユ垚鍔燂紝ServiceOrdID: " + serviceOrdId);
+            } else {
+                return error("鏈嶅姟鍗曞悓姝ュけ璐ワ紝璇锋煡鐪嬪悓姝ラ敊璇俊鎭�");
+            }
+            
+        } catch (Exception e) {
+            logger.error("鎵嬪姩鍚屾鏈嶅姟鍗曞紓甯革紝taskId: {}", taskId, e);
+            return error("鍚屾寮傚父: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 鎵嬪姩鍚屾璋冨害鍗曞埌鏃х郴缁�
+     * 褰撹皟搴﹀崟鍚屾澶辫触鎴栨湭鍚屾鏃讹紝鍙互閫氳繃姝ゆ帴鍙f墜鍔ㄨЕ鍙戝悓姝�
+     */
+    @PreAuthorize("@ss.hasPermi('task:general:edit')")
+    @Log(title = "鎵嬪姩鍚屾璋冨害鍗�", businessType = BusinessType.UPDATE)
+    @PostMapping("/syncDispatchOrder/{taskId}")
+    public AjaxResult syncDispatchOrder(@PathVariable Long taskId) {
+        try {
+            // 鏌ヨ浠诲姟淇℃伅
+            SysTask task = sysTaskService.selectSysTaskByTaskId(taskId);
+            if (task == null) {
+                return error("浠诲姟涓嶅瓨鍦�");
+            }
+            
+            // 鍙敮鎸佹�ユ晳杞繍浠诲姟
+            if (!"EMERGENCY_TRANSFER".equals(task.getTaskType())) {
+                return error("鍙湁鎬ユ晳杞繍浠诲姟鎵嶈兘鍚屾鍒版棫绯荤粺");
+            }
+            
+            // 鏌ヨ鎬ユ晳杞繍鎵╁睍淇℃伅
+            SysTaskEmergency emergency = sysTaskEmergencyService.selectSysTaskEmergencyByTaskId(taskId);
+            if (emergency == null) {
+                return error("鎬ユ晳杞繍鎵╁睍淇℃伅涓嶅瓨鍦�");
+            }
+            
+            // 蹇呴』鍏堟湁鏈嶅姟鍗�
+            if (emergency.getLegacyServiceOrdId() == null || emergency.getLegacyServiceOrdId() <= 0) {
+                return error("璇峰厛鍚屾鏈嶅姟鍗�");
+            }
+            
+            // 璋冪敤鍚屾鏈嶅姟
+            Long dispatchOrdId = taskDispatchSyncService.syncDispatch(taskId);
+            
+            if (dispatchOrdId != null && dispatchOrdId > 0) {
+                return success("璋冨害鍗曞悓姝ユ垚鍔燂紝DispatchOrdID: " + dispatchOrdId);
+            } else {
+                return error("璋冨害鍗曞悓姝ュけ璐ワ紝璇锋煡鐪嬪悓姝ラ敊璇俊鎭�");
+            }
+            
+        } catch (Exception e) {
+            logger.error("鎵嬪姩鍚屾璋冨害鍗曞紓甯革紝taskId: {}", taskId, e);
+            return error("鍚屾寮傚父: " + e.getMessage());
+        }
+    }
 }
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/HospitalTokenizerUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/HospitalTokenizerUtil.java
index 839d971..bb7c8ba 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/HospitalTokenizerUtil.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/HospitalTokenizerUtil.java
@@ -25,7 +25,7 @@
     private static final Set<String> STOP_WORDS = new HashSet<>(Arrays.asList(
         "鍖婚櫌", "璇婃墍", "鍗敓", "闀�", "涔�", 
         "琛楅亾", "璺�", "鍙�", "鏍�", "鍗曞厓", "瀹�", "灞�", "妤�", "鐨�", "浜�", 
-        "鍦�", "涓�", "鍜�", "鍙�", "绛�", "涔�", "浜�", "涓�", "鏈�", "鏃�"
+        "鍦�", "涓�", "鍜�", "鍙�", "绛�", "涔�", "浜�", "涓�", "鏈�", "鏃�","(",")","锛�","锛�","銆�","锛�","銆�","锛�","锛�","锛�","锛�","鈥�","鈥�","鈥�","鈥�"
     ));
     
     /**
@@ -35,8 +35,10 @@
     private static final Set<String> HIGH_WEIGHT_WORDS = new HashSet<>(Arrays.asList(
         "浜烘皯", "涓尰", "涓タ鍖�", "涓タ鍖荤粨鍚�", "鍖荤枟", "濡囧辜", "鍎跨", "鑲ょ", 
         "鍙h厰", "鐪肩", "楠ㄧ", "鏁村舰", "绮剧", "搴峰", "鎬ユ晳", "鍖诲闄�", 
-        "鍖荤澶у", "涓撶", "绗竴", "绗簩", "绗笁", "绗洓", "绗簲",
-        "鍐涘尯", "鍐涘尰", "涓績", "闄勫睘", "鐪佺珛", "甯傜珛", "鍖虹珛"
+        "鍖荤澶у", "涓撶", 
+        "鍐涘尯", "鍐涘尰", "涓績", "闄勫睘", "鐪佺珛", "甯傜珛", "鍖虹珛", "鑴戠", "鎬婚櫌", "鎱堝杽", "淇濆仴闄�", "鍙h厰", "绁堢", "鐪肩", "閾佽矾", "闄勪竴", "闄勪簩", "闄勪笁", "闄勫洓", "闄勪簲", "闄勫叚",
+            "绗竴", "绗簩", "绗笁", "绗洓", "绗簲", "绗叚", "绗竷", "绗叓", "绗節", "绗崄",
+            "鑲跨槫"
     ));
 
     /**
@@ -46,7 +48,7 @@
     private static final Set<String> HOSPITAL_KEYWORD_DICT = new HashSet<>(Arrays.asList(
         "涓尰闄�", "涓尰鍖婚櫌", "甯傚尰闄�", "鐪佸尰闄�", "浜烘皯鍖婚櫌", "涓績鍖婚櫌", "鍙h厰鍖婚櫌",
         "鍗庝鲸鍖婚櫌", "鍎跨鍖婚櫌", "鐪肩涓績", "绂忓埄闄�", "闂ㄨ瘖閮�", "涓北澶у", "闄勫睘鍖婚櫌",
-        "瀛欓�镐粰"
+        "瀛欓�镐粰","闂ㄨ瘖"
     ));
 
     /** 缁勫悎璇嶇敓鎴愮殑鏈�灏忓瓧绗﹂暱搴� */
@@ -568,8 +570,11 @@
         
         // 鍒嗛櫌鐗瑰緛鍏抽敭璇�
         String[] branchKeywords = {
-            "鍒嗛櫌", "鍒嗛儴", "闂ㄨ瘖閮�", "绀惧尯鍗敓", "鍗敓绔�", "鍗敓鏈嶅姟涓績",
-            "涓滈櫌", "瑗块櫌", "鍗楅櫌", "鍖楅櫌", "鏂伴櫌", "鑰侀櫌"
+            "鍒嗛櫌", "鍒嗛儴", "闂ㄨ瘖閮�","闂ㄨ瘖", "绀惧尯鍗敓", "鍗敓绔�", "鍗敓鏈嶅姟涓績",
+            "涓滈櫌", "瑗块櫌", "鍗楅櫌", "鍖楅櫌", "鏂伴櫌", "鑰侀櫌",
+           "浜烘皯鍖婚櫌","闄勫睘鍖婚櫌","绂忓埄闄�","鍒嗛櫌"
+            
+            
         };
         
         for (String keyword : branchKeywords) {
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/image/ImageCompressUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/image/ImageCompressUtil.java
new file mode 100644
index 0000000..a79f960
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/image/ImageCompressUtil.java
@@ -0,0 +1,374 @@
+package com.ruoyi.common.utils.image;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
+import javax.imageio.stream.ImageOutputStream;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+
+/**
+ * 鍥剧墖鍘嬬缉宸ュ叿绫�
+ * 涓撲负OCR璇嗗埆浼樺寲锛屽湪淇濊瘉鏂囧瓧娓呮櫚搴︾殑鍓嶆彁涓嬪帇缂╁浘鐗囧ぇ灏�
+ * 
+ * @author ruoyi
+ * @date 2025-01-20
+ */
+public class ImageCompressUtil {
+    
+    private static final Logger log = LoggerFactory.getLogger(ImageCompressUtil.class);
+    
+    /**
+     * 榛樿鏈�澶ф枃浠跺ぇ灏忥紙10MB锛�
+     */
+    public static final long DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
+    
+    /**
+     * OCR鏈�浣宠瘑鍒垎杈ㄧ巼锛堟渶灏忚竟鍍忕礌锛�
+     */
+    public static final int OCR_OPTIMAL_MIN_SIZE = 1500;
+    
+    /**
+     * 楂樿川閲忓帇缂╄川閲忓弬鏁帮紙0.90 = 90%璐ㄩ噺锛�
+     */
+    public static final float HIGH_QUALITY = 0.90f;
+    
+    /**
+     * 涓瓑璐ㄩ噺鍘嬬缉璐ㄩ噺鍙傛暟锛�0.80 = 80%璐ㄩ噺锛�
+     */
+    public static final float MEDIUM_QUALITY = 0.80f;
+    
+    /**
+     * 浣庤川閲忓帇缂╄川閲忓弬鏁帮紙0.75 = 75%璐ㄩ噺锛�
+     */
+    public static final float LOW_QUALITY = 0.75f;
+    
+    /**
+     * 鏅鸿兘鍘嬬缉鍥剧墖锛堥拡瀵筄CR浼樺寲锛�
+     * 浣跨敤榛樿3MB闄愬埗
+     * 
+     * @param file 涓婁紶鐨勫浘鐗囨枃浠�
+     * @return 鍘嬬缉鍚庣殑涓存椂鏂囦欢
+     * @throws IOException IO寮傚父
+     */
+    public static File compressForOCR(MultipartFile file) throws IOException {
+        return compressForOCR(file, DEFAULT_MAX_FILE_SIZE);
+    }
+    
+    /**
+     * 鏅鸿兘鍘嬬缉鍥剧墖锛堥拡瀵筄CR浼樺寲锛�
+     * 淇濊瘉璇嗗埆鍑嗙‘鐜囩殑鍚屾椂鍘嬬缉鍒版寚瀹氬ぇ灏忎互涓�
+     * 
+     * @param file 涓婁紶鐨勫浘鐗囨枃浠�
+     * @param maxSize 鏈�澶ф枃浠跺ぇ灏忥紙瀛楄妭锛�
+     * @return 鍘嬬缉鍚庣殑涓存椂鏂囦欢
+     * @throws IOException IO寮傚父
+     */
+    public static File compressForOCR(MultipartFile file, long maxSize) throws IOException {
+        long fileSize = file.getSize();
+        
+        // 濡傛灉鏂囦欢灏忎簬闄愬埗锛岀洿鎺ヤ繚瀛�
+        if (fileSize <= maxSize) {
+            log.debug("鍥剧墖澶у皬 {} KB 鏈秴杩囬檺鍒� {} KB锛屾棤闇�鍘嬬缉", 
+                fileSize / 1024.0, maxSize / 1024.0);
+            return saveToTempFile(file);
+        }
+        
+        log.info("鍥剧墖澶у皬 {} MB 瓒呰繃闄愬埗 {} MB锛屽紑濮嬫櫤鑳藉帇缂�...", 
+            fileSize / 1024.0 / 1024.0, maxSize / 1024.0 / 1024.0);
+        
+        // 璇诲彇鍘熷鍥剧墖
+        BufferedImage originalImage = ImageIO.read(file.getInputStream());
+        if (originalImage == null) {
+            throw new IOException("鏃犳硶璇诲彇鍥剧墖鏂囦欢锛屽彲鑳芥牸寮忎笉鏀寔");
+        }
+        
+        int originalWidth = originalImage.getWidth();
+        int originalHeight = originalImage.getHeight();
+        log.info("鍘熷鍥剧墖灏哄: {}x{}", originalWidth, originalHeight);
+        
+        // 绛栫暐1锛氬厛灏濊瘯楂樿川閲廕PEG鍘嬬缉锛堜笉鏀瑰彉灏哄锛�
+        File compressedFile = compressWithQuality(originalImage, file.getOriginalFilename(), HIGH_QUALITY);
+        
+        if (compressedFile.length() <= maxSize) {
+            log.info("鍘嬬缉鎴愬姛锛堥珮璐ㄩ噺鍘嬬缉锛夛紝鏂囦欢澶у皬: {} MB", 
+                compressedFile.length() / 1024.0 / 1024.0);
+            return compressedFile;
+        }
+        
+        // 绛栫暐2锛氬鏋滆繕鏄お澶э紝閫傚害缂╁皬灏哄锛堜繚璇丱CR璇嗗埆锛�
+        double scaleFactor = calculateScaleFactor(originalWidth, originalHeight, maxSize, compressedFile.length());
+        
+        if (scaleFactor < 1.0) {
+            int newWidth = (int) (originalWidth * scaleFactor);
+            int newHeight = (int) (originalHeight * scaleFactor);
+            log.info("璋冩暣鍥剧墖灏哄鑷�: {}x{} (缂╂斁姣斾緥: {}%)", 
+                newWidth, newHeight, (int)(scaleFactor * 100));
+            
+            BufferedImage resizedImage = resizeImageHighQuality(originalImage, newWidth, newHeight);
+            compressedFile.delete(); // 鍒犻櫎涔嬪墠鐨勪复鏃舵枃浠�
+            compressedFile = compressWithQuality(resizedImage, file.getOriginalFilename(), HIGH_QUALITY);
+        }
+        
+        // 绛栫暐3锛氬鏋滆繕鏄お澶э紝闄嶄綆鍘嬬缉璐ㄩ噺锛堟渶鍚庢墜娈碉級
+        if (compressedFile.length() > maxSize) {
+            log.warn("灏哄璋冩暣鍚庝粛瓒呴檺锛岄檷浣庡帇缂╄川閲�");
+            BufferedImage finalImage;
+            if (scaleFactor < 1.0) {
+                int newWidth = (int) (originalWidth * scaleFactor);
+                int newHeight = (int) (originalHeight * scaleFactor);
+                finalImage = resizeImageHighQuality(originalImage, newWidth, newHeight);
+            } else {
+                finalImage = originalImage;
+            }
+            
+            compressedFile.delete(); // 鍒犻櫎涔嬪墠鐨勪复鏃舵枃浠�
+            
+            // 灏濊瘯涓瓑璐ㄩ噺
+            compressedFile = compressWithQuality(finalImage, file.getOriginalFilename(), MEDIUM_QUALITY);
+            
+            // 濡傛灉杩樹笉琛岋紝浣跨敤浣庤川閲�
+            if (compressedFile.length() > maxSize) {
+                log.warn("浣跨敤浣庤川閲忓帇缂╋紙75%锛�");
+                compressedFile.delete();
+                compressedFile = compressWithQuality(finalImage, file.getOriginalFilename(), LOW_QUALITY);
+            }
+        }
+        
+        long finalSize = compressedFile.length();
+        double compressionRatio = (1 - (double)finalSize / fileSize) * 100;
+        log.info("鏈�缁堝帇缂╁畬鎴愶紝鏂囦欢澶у皬: {} MB (鍘嬬缉鐜�: {}%)", 
+            finalSize / 1024.0 / 1024.0, (int)compressionRatio);
+        
+        if (finalSize > maxSize) {
+            log.warn("璀﹀憡锛氬帇缂╁悗鏂囦欢澶у皬 {} MB 浠嶈秴杩囬檺鍒� {} MB", 
+                finalSize / 1024.0 / 1024.0, maxSize / 1024.0 / 1024.0);
+        }
+        
+        return compressedFile;
+    }
+    
+    /**
+     * 浣跨敤鎸囧畾璐ㄩ噺鍘嬬缉鍥剧墖涓篔PEG鏍煎紡
+     * 
+     * @param image 鍥剧墖瀵硅薄
+     * @param originalFilename 鍘熷鏂囦欢鍚�
+     * @param quality 鍘嬬缉璐ㄩ噺锛�0.0-1.0锛�
+     * @return 鍘嬬缉鍚庣殑鏂囦欢
+     * @throws IOException IO寮傚父
+     */
+    public static File compressWithQuality(BufferedImage image, String originalFilename, float quality) throws IOException {
+        if (quality < 0.0f || quality > 1.0f) {
+            throw new IllegalArgumentException("鍘嬬缉璐ㄩ噺蹇呴』鍦�0.0鍒�1.0涔嬮棿");
+        }
+        
+        // 杞崲涓篟GB鏍煎紡锛圝PEG涓嶆敮鎸侀�忔槑搴︼級
+        BufferedImage rgbImage = convertToRGB(image);
+        
+        // 鍒涘缓涓存椂鏂囦欢
+        String tempDir = System.getProperty("java.io.tmpdir");
+        String filename = System.currentTimeMillis() + "_compressed_" + 
+            getJpegFilename(originalFilename);
+        File outputFile = new File(tempDir, filename);
+        
+        // 浣跨敤ImageWriter杩涜楂樿川閲忓帇缂�
+        Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
+        if (!writers.hasNext()) {
+            throw new IOException("绯荤粺涓嶆敮鎸丣PEG缂栫爜");
+        }
+        
+        ImageWriter writer = writers.next();
+        ImageWriteParam param = writer.getDefaultWriteParam();
+        
+        // 璁剧疆鍘嬬缉妯″紡鍜岃川閲�
+        param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+        param.setCompressionQuality(quality);
+        
+        try (ImageOutputStream ios = ImageIO.createImageOutputStream(outputFile)) {
+            writer.setOutput(ios);
+            writer.write(null, new IIOImage(rgbImage, null, null), param);
+        } finally {
+            writer.dispose();
+        }
+        
+        log.debug("鍘嬬缉瀹屾垚锛岃川閲�: {}%, 鏂囦欢澶у皬: {} KB", 
+            (int)(quality * 100), outputFile.length() / 1024.0);
+        
+        return outputFile;
+    }
+    
+    /**
+     * 楂樿川閲忓浘鐗囩缉鏀撅紙涓撲负OCR浼樺寲锛�
+     * 浣跨敤鍙屼笁娆℃彃鍊肩畻娉曚繚璇佹枃瀛楁竻鏅板害
+     * 
+     * @param originalImage 鍘熷鍥剧墖
+     * @param targetWidth 鐩爣瀹藉害
+     * @param targetHeight 鐩爣楂樺害
+     * @return 缂╂斁鍚庣殑鍥剧墖
+     */
+    public static BufferedImage resizeImageHighQuality(BufferedImage originalImage, int targetWidth, int targetHeight) {
+        if (targetWidth <= 0 || targetHeight <= 0) {
+            throw new IllegalArgumentException("鐩爣瀹藉害鍜岄珮搴﹀繀椤诲ぇ浜�0");
+        }
+        
+        BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
+        Graphics2D g2d = resizedImage.createGraphics();
+        
+        try {
+            // 璁剧疆楂樿川閲忔覆鏌撳弬鏁帮紙鍏抽敭锛侊級
+            g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+            g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+            g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+            g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
+            
+            g2d.drawImage(originalImage, 0, 0, targetWidth, targetHeight, null);
+        } finally {
+            g2d.dispose();
+        }
+        
+        return resizedImage;
+    }
+    
+    /**
+     * 閫氱敤鍥剧墖鍘嬬缉锛堟寜鐩爣瀹介珮锛�
+     * 
+     * @param file 涓婁紶鐨勫浘鐗囨枃浠�
+     * @param targetWidth 鐩爣瀹藉害
+     * @param targetHeight 鐩爣楂樺害
+     * @param quality 鍘嬬缉璐ㄩ噺锛�0.0-1.0锛�
+     * @return 鍘嬬缉鍚庣殑鏂囦欢
+     * @throws IOException IO寮傚父
+     */
+    public static File compress(MultipartFile file, int targetWidth, int targetHeight, float quality) throws IOException {
+        BufferedImage originalImage = ImageIO.read(file.getInputStream());
+        if (originalImage == null) {
+            throw new IOException("鏃犳硶璇诲彇鍥剧墖鏂囦欢");
+        }
+        
+        BufferedImage resizedImage = resizeImageHighQuality(originalImage, targetWidth, targetHeight);
+        return compressWithQuality(resizedImage, file.getOriginalFilename(), quality);
+    }
+    
+    /**
+     * 鎸夋瘮渚嬪帇缂╁浘鐗�
+     * 
+     * @param file 涓婁紶鐨勫浘鐗囨枃浠�
+     * @param scale 缂╂斁姣斾緥锛�0.0-1.0锛�
+     * @param quality 鍘嬬缉璐ㄩ噺锛�0.0-1.0锛�
+     * @return 鍘嬬缉鍚庣殑鏂囦欢
+     * @throws IOException IO寮傚父
+     */
+    public static File compressByScale(MultipartFile file, double scale, float quality) throws IOException {
+        if (scale <= 0.0 || scale > 1.0) {
+            throw new IllegalArgumentException("缂╂斁姣斾緥蹇呴』鍦�0.0鍒�1.0涔嬮棿");
+        }
+        
+        BufferedImage originalImage = ImageIO.read(file.getInputStream());
+        if (originalImage == null) {
+            throw new IOException("鏃犳硶璇诲彇鍥剧墖鏂囦欢");
+        }
+        
+        int newWidth = (int) (originalImage.getWidth() * scale);
+        int newHeight = (int) (originalImage.getHeight() * scale);
+        
+        BufferedImage resizedImage = resizeImageHighQuality(originalImage, newWidth, newHeight);
+        return compressWithQuality(resizedImage, file.getOriginalFilename(), quality);
+    }
+    
+    /**
+     * 璁$畻鏈�浣崇缉鏀炬瘮渚�
+     * 
+     * @param width 鍘熷瀹藉害
+     * @param height 鍘熷楂樺害
+     * @param targetSize 鐩爣鏂囦欢澶у皬
+     * @param currentSize 褰撳墠鏂囦欢澶у皬
+     * @return 缂╂斁姣斾緥
+     */
+    private static double calculateScaleFactor(int width, int height, long targetSize, long currentSize) {
+        int minDimension = Math.min(width, height);
+        double scaleFactor = 1.0;
+        
+        // 鍩轰簬鏂囦欢澶у皬浼扮畻缂╂斁姣斾緥
+        double sizeRatio = Math.sqrt((double) targetSize / currentSize);
+        
+        // 淇濇姢鏈�灏忓昂瀵革紙淇濊瘉OCR璇嗗埆锛�
+        if (minDimension > 2000) {
+            // 澶у浘鐗囷紝鍙互閫傚害缂╁皬
+            scaleFactor = Math.min(sizeRatio, 2000.0 / minDimension);
+        } else if (minDimension > OCR_OPTIMAL_MIN_SIZE) {
+            // 涓瓑鍥剧墖锛岃交寰缉灏�
+            scaleFactor = Math.min(sizeRatio, (double) OCR_OPTIMAL_MIN_SIZE / minDimension);
+        } else {
+            // 灏忓浘鐗囷紝灏介噺涓嶇缉灏�
+            scaleFactor = Math.min(sizeRatio, 0.95);
+        }
+        
+        return scaleFactor;
+    }
+    
+    /**
+     * 灏嗗浘鐗囪浆鎹负RGB鏍煎紡
+     * 
+     * @param image 鍘熷鍥剧墖
+     * @return RGB鏍煎紡鍥剧墖
+     */
+    private static BufferedImage convertToRGB(BufferedImage image) {
+        if (image.getType() == BufferedImage.TYPE_INT_RGB) {
+            return image;
+        }
+        
+        BufferedImage rgbImage = new BufferedImage(
+            image.getWidth(), 
+            image.getHeight(), 
+            BufferedImage.TYPE_INT_RGB
+        );
+        Graphics2D g = rgbImage.createGraphics();
+        try {
+            g.drawImage(image, 0, 0, null);
+        } finally {
+            g.dispose();
+        }
+        
+        return rgbImage;
+    }
+    
+    /**
+     * 灏嗘枃浠跺悕杞崲涓篔PEG鏍煎紡
+     * 
+     * @param originalFilename 鍘熷鏂囦欢鍚�
+     * @return JPEG鏂囦欢鍚�
+     */
+    private static String getJpegFilename(String originalFilename) {
+        if (originalFilename == null || originalFilename.isEmpty()) {
+            return "image.jpg";
+        }
+        
+        return originalFilename.replaceAll("\\.(png|PNG|gif|GIF|bmp|BMP|webp|WEBP)$", ".jpg");
+    }
+    
+    /**
+     * 淇濆瓨MultipartFile鍒颁复鏃舵枃浠�
+     * 
+     * @param file 涓婁紶鐨勬枃浠�
+     * @return 涓存椂鏂囦欢
+     * @throws IOException IO寮傚父
+     */
+    private static File saveToTempFile(MultipartFile file) throws IOException {
+        String tempDir = System.getProperty("java.io.tmpdir");
+        String originalFilename = file.getOriginalFilename();
+        File tempFile = new File(tempDir, System.currentTimeMillis() + "_" + originalFilename);
+        file.transferTo(tempFile);
+        return tempFile;
+    }
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/image/package-info.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/image/package-info.java
new file mode 100644
index 0000000..e8f5406
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/image/package-info.java
@@ -0,0 +1,63 @@
+/**
+ * 鍥剧墖澶勭悊宸ュ叿鍖�
+ * 
+ * <h2>涓昏鍔熻兘</h2>
+ * <ul>
+ *   <li>鏅鸿兘鍥剧墖鍘嬬缉锛堜笓涓篛CR浼樺寲锛�</li>
+ *   <li>楂樿川閲忓浘鐗囩缉鏀�</li>
+ *   <li>鍥剧墖鏍煎紡杞崲</li>
+ * </ul>
+ * 
+ * <h2>浣跨敤绀轰緥</h2>
+ * 
+ * <h3>1. OCR鍥剧墖鏅鸿兘鍘嬬缉锛堟帹鑽愶級</h3>
+ * <pre>
+ * // 鑷姩鍘嬬缉鍒�3MB浠ヤ笅锛屼繚璇丱CR璇嗗埆鍑嗙‘鐜�
+ * File compressedFile = ImageCompressUtil.compressForOCR(multipartFile);
+ * 
+ * // 鑷畾涔夊ぇ灏忛檺鍒�
+ * File compressedFile = ImageCompressUtil.compressForOCR(multipartFile, 5 * 1024 * 1024); // 5MB
+ * </pre>
+ * 
+ * <h3>2. 鎸夊昂瀵稿帇缂�</h3>
+ * <pre>
+ * // 鍘嬬缉鍒版寚瀹氬楂�
+ * File compressedFile = ImageCompressUtil.compress(multipartFile, 1920, 1080, 0.85f);
+ * </pre>
+ * 
+ * <h3>3. 鎸夋瘮渚嬪帇缂�</h3>
+ * <pre>
+ * // 缂╁皬鍒板師鏉ョ殑50%
+ * File compressedFile = ImageCompressUtil.compressByScale(multipartFile, 0.5, 0.90f);
+ * </pre>
+ * 
+ * <h3>4. 楂樿川閲忓浘鐗囩缉鏀�</h3>
+ * <pre>
+ * BufferedImage originalImage = ImageIO.read(file);
+ * BufferedImage resizedImage = ImageCompressUtil.resizeImageHighQuality(originalImage, 800, 600);
+ * </pre>
+ * 
+ * <h3>5. 鑷畾涔夎川閲忓帇缂�</h3>
+ * <pre>
+ * BufferedImage image = ImageIO.read(file);
+ * File compressed = ImageCompressUtil.compressWithQuality(image, "photo.jpg", 0.80f); // 80%璐ㄩ噺
+ * </pre>
+ * 
+ * <h2>鍘嬬缉璐ㄩ噺甯搁噺</h2>
+ * <ul>
+ *   <li>{@code ImageCompressUtil.HIGH_QUALITY} - 0.90锛堥珮璐ㄩ噺锛屾帹鑽怬CR浣跨敤锛�</li>
+ *   <li>{@code ImageCompressUtil.MEDIUM_QUALITY} - 0.80锛堜腑绛夎川閲忥級</li>
+ *   <li>{@code ImageCompressUtil.LOW_QUALITY} - 0.75锛堜綆璐ㄩ噺锛�</li>
+ * </ul>
+ * 
+ * <h2>娉ㄦ剰浜嬮」</h2>
+ * <ul>
+ *   <li>鍘嬬缉鍚庣殑鏂囦欢淇濆瓨鍦ㄧ郴缁熶复鏃剁洰褰曪紝浣跨敤瀹屾瘯鍚庨渶鎵嬪姩鍒犻櫎</li>
+ *   <li>OCR鍘嬬缉浼氫繚璇佹渶灏忚竟涓嶅皬浜�1500px锛岀‘淇濇枃瀛楁竻鏅�</li>
+ *   <li>鏀寔PNG銆丅MP銆丟IF绛夋牸寮忚嚜鍔ㄨ浆涓篔PEG</li>
+ * </ul>
+ * 
+ * @author ruoyi
+ * @since 2025-01-20
+ */
+package com.ruoyi.common.utils.image;
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/OCRController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/OCRController.java
index d370b89..57da753 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/OCRController.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/OCRController.java
@@ -4,6 +4,7 @@
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.utils.file.FileUploadUtils;
+import com.ruoyi.common.utils.image.ImageCompressUtil;
 import com.ruoyi.system.utils.AliOCRUtil;
 import com.ruoyi.system.utils.BaiduOCRUtil;
 import com.ruoyi.system.utils.TencentOCRUtil;
@@ -55,11 +56,8 @@
                 return error("涓嶆敮鎸佺殑璇嗗埆绫诲瀷: " + type + ", 鏀寔鐨勭被鍨�: " + String.join(",", SUPPORTED_TYPES));
             }
 
-            // 淇濆瓨涓存椂鏂囦欢
-            String tempDir = System.getProperty("java.io.tmpdir");
-            String originalFilename = file.getOriginalFilename();
-            File tempFile = new File(tempDir, System.currentTimeMillis() + "_" + originalFilename);
-            file.transferTo(tempFile);
+            // 鏅鸿兘鍘嬬缉鍥剧墖锛堣嚜鍔ㄥ鐞嗚秴杩�3MB鐨勫浘鐗囷級
+            File tempFile = ImageCompressUtil.compressForOCR(file);
 
             // 鏍规嵁鎻愪緵鍟嗚皟鐢ㄤ笉鍚岀殑OCR鏈嶅姟
             JSONObject ocrResult;
@@ -92,7 +90,10 @@
             // 鏋勫缓杩斿洖缁撴灉
             Map<String, Object> result = new HashMap<>();
             result.put("ocrResult", ocrResult);
-            result.put("fileName", originalFilename);
+            result.put("fileName", file.getOriginalFilename());
+            result.put("originalSize", file.getSize());
+            result.put("processedSize", tempFile.length());
+            result.put("compressed", file.getSize() > tempFile.length());
             result.put("type", type);
             result.put("provider", provider);
 
@@ -263,11 +264,8 @@
                 return error("涓婁紶鍥剧墖涓嶈兘涓虹┖");
             }
 
-            // 淇濆瓨涓存椂鏂囦欢
-            String tempDir = System.getProperty("java.io.tmpdir");
-            String originalFilename = file.getOriginalFilename();
-            File tempFile = new File(tempDir, System.currentTimeMillis() + "_" + originalFilename);
-            file.transferTo(tempFile);
+            // 鏅鸿兘鍘嬬缉鍥剧墖锛堣嚜鍔ㄥ鐞嗚秴杩�3MB鐨勫浘鐗囷級
+            File tempFile = ImageCompressUtil.compressForOCR(file);
 
             // 璋冪敤鑵捐浜戞墜鍐欎綋璇嗗埆
             Map<String, String> resultMap = TencentOCRUtil.handwritingRecognizeWith(tempFile.getAbsolutePath(), itemNames);
@@ -282,7 +280,7 @@
 
             // 鏋勫缓杩斿洖缁撴灉
             Map<String, Object> result = new HashMap<>();
-            result.put("fileName", originalFilename);
+            result.put("fileName", file.getOriginalFilename());
             result.put("type", "HandWriting");
             result.put("provider", "tencent");
             result.put("fields", resultMap);
@@ -356,11 +354,8 @@
                 }
 
                 try {
-                    // 淇濆瓨涓存椂鏂囦欢
-                    String tempDir = System.getProperty("java.io.tmpdir");
-                    String originalFilename = file.getOriginalFilename();
-                    File tempFile = new File(tempDir, System.currentTimeMillis() + "_" + originalFilename);
-                    file.transferTo(tempFile);
+                    // 鏅鸿兘鍘嬬缉鍥剧墖锛堣嚜鍔ㄥ鐞嗚秴杩�3MB鐨勫浘鐗囷級
+                    File tempFile = ImageCompressUtil.compressForOCR(file);
 
                     // 璋冪敤鑵捐浜戞墜鍐欎綋璇嗗埆
                     Map<String, String> resultMap = TencentOCRUtil.handwritingRecognizeWith(tempFile.getAbsolutePath(), itemNames);
@@ -371,8 +366,8 @@
                     // 妫�鏌ユ槸鍚︽湁閿欒
                     if (resultMap.containsKey("error")) {
                         failCount++;
-                        errorMessages.append(originalFilename).append(":").append(resultMap.get("error")).append("; ");
-                        logger.warn("鍥剧墖 {} 璇嗗埆澶辫触: {}", originalFilename, resultMap.get("error"));
+                        errorMessages.append(file.getOriginalFilename()).append(":").append(resultMap.get("error")).append("; ");
+                        logger.warn("鍥剧墖 {} 璇嗗埆澶辫触: {}", file.getOriginalFilename(), resultMap.get("error"));
                     } else {
                         // 鍚堝苟璇嗗埆缁撴灉锛堝鏋渒ey宸插瓨鍦紝涓嶈鐩栵級
                         for (Map.Entry<String, String> entry : resultMap.entrySet()) {
@@ -381,7 +376,7 @@
                             }
                         }
                         successCount++;
-                        logger.info("鍥剧墖 {} 璇嗗埆鎴愬姛锛屾彁鍙� {} 涓瓧娈�", originalFilename, resultMap.size());
+                        logger.info("鍥剧墖 {} 璇嗗埆鎴愬姛锛屾彁鍙� {} 涓瓧娈�", file.getOriginalFilename(), resultMap.size());
                     }
                 } catch (Exception e) {
                     failCount++;
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserSyncServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserSyncServiceImpl.java
index b8c9b2e..e200cec 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserSyncServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserSyncServiceImpl.java
@@ -65,7 +65,7 @@
                 return AjaxResult.warn("浼犲叆鐨勭敤鎴锋暟鎹负绌�");
             }
             
-            log.info("寮�濮嬪悓姝� {} 鏉A鐢ㄦ埛鏁版嵁鍒� MySQL 鏁版嵁搴�...", oaUsers.size());
+//            log.info("寮�濮嬪悓姝� {} 鏉A鐢ㄦ埛鏁版嵁鍒� MySQL 鏁版嵁搴�...", oaUsers.size());
             
             int createdCount = 0;
             int updatedCount = 0;
@@ -109,8 +109,8 @@
                         // 鐢ㄦ埛宸插瓨鍦紝鏇存柊淇℃伅
                         updateExistingUser(existingUser, dto, deptId);
                         updatedCount++;
-                        log.info("鏇存柊鐢ㄦ埛: {} ({}), oaUserId: {}", 
-                            dto.getNickName(), dto.getUserName(), dto.getOaUserId());
+//                        log.info("鏇存柊鐢ㄦ埛: {} ({}), oaUserId: {}",
+//                            dto.getNickName(), dto.getUserName(), dto.getOaUserId());
                     }
                     else
                     {
@@ -140,16 +140,16 @@
                             userByName.setUpdateBy("sync");
                             sysUserMapper.updateUser(userByName);
                             updatedCount++;
-                            log.info("鏇存柊宸插瓨鍦ㄧ敤鎴峰悕鐨勭敤鎴�: {} ({}), 璁剧疆oaUserId: {}", 
-                                dto.getNickName(), dto.getUserName(), dto.getOaUserId());
+//                            log.info("鏇存柊宸插瓨鍦ㄧ敤鎴峰悕鐨勭敤鎴�: {} ({}), 璁剧疆oaUserId: {}",
+//                                dto.getNickName(), dto.getUserName(), dto.getOaUserId());
                         }
                         else
                         {
                             // 鍒涘缓鏂扮敤鎴�
                             createNewUser(dto, deptId);
                             createdCount++;
-                            log.info("鍒涘缓鏂扮敤鎴�: {} ({}), oaUserId: {}, deptId: {}", 
-                                dto.getNickName(), dto.getUserName(), dto.getOaUserId(), deptId);
+//                            log.info("鍒涘缓鏂扮敤鎴�: {} ({}), oaUserId: {}, deptId: {}",
+//                                dto.getNickName(), dto.getUserName(), dto.getOaUserId(), deptId);
                         }
                     }
                 }
@@ -163,7 +163,7 @@
 
             String message = String.format("鍚屾瀹屾垚锛佸垱寤虹敤鎴�: %d, 鏇存柊鐢ㄦ埛: %d, 璺宠繃: %d, 澶辫触: %d",
                 createdCount, updatedCount, skippedCount, errorCount);
-            log.info(message);
+//            log.info(message);
 
             Map<String, Object> result = new HashMap<>();
             result.put("created", createdCount);
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/TencentOCRUtil.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/TencentOCRUtil.java
index 6597b02..2f0ea18 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/utils/TencentOCRUtil.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/TencentOCRUtil.java
@@ -18,10 +18,8 @@
 import org.springframework.stereotype.Component;
 
 import java.io.File;
-import java.util.Base64;
+import java.util.*;
 import java.nio.file.Files;
-import java.util.HashMap;
-import java.util.Map;
 
 /**
  * 鑵捐浜慜CR宸ュ叿绫�
@@ -222,7 +220,7 @@
             // {"鎮h�呯鍚嶏紙鎵嬪嵃锛�", "绛惧瓧浜鸿韩浠借瘉鍙风爜", "鏃ユ湡", "鑱旂郴鐢佃瘽", "鏈汉", "绛惧瓧浜轰笌鎮h�呭叧绯�"}
             req.setItemNames(itemNames != null ? itemNames : new String[]{"鎮h�呭鍚�", "鎬у埆", "骞撮緞", "韬唤璇佸彿", "璇婃柇", "闇�鏀粯杞繍璐圭敤", "琛岀▼", "寮�濮嬫椂闂�", "缁撴潫鏃堕棿", "瀹跺睘绛惧悕"});
             req.setOutputLanguage("cn");
-            req.setReturnFullText(false);
+            req.setReturnFullText(true);
             req.setItemNamesShowMode(false);
             ExtractDocMultiResponse resp = client.ExtractDocMulti(req);
 
@@ -295,7 +293,42 @@
                     }
                 }
             }
-            
+            //灏� WordList
+            List<String> wordListResult = new ArrayList<>();
+            if (responseData.containsKey("WordList") && responseData.getJSONArray("WordList") != null){
+                JSONArray wordList = responseData.getJSONArray("WordList");
+                for (int i = 0; i < wordList.size(); i++) {
+                    JSONObject word = wordList.getJSONObject(i);
+                    // {
+                    //        "Coord": {
+                    //          "LeftBottom": {
+                    //            "X": 472,
+                    //            "Y": 1500
+                    //          },
+                    //          "LeftTop": {
+                    //            "X": 467,
+                    //            "Y": 1420
+                    //          },
+                    //          "RightBottom": {
+                    //            "X": 636,
+                    //            "Y": 1490
+                    //          },
+                    //          "RightTop": {
+                    //            "X": 631,
+                    //            "Y": 1410
+                    //          }
+                    //        },
+                    //        "DetectedText": "琛岀▼:"
+                    //      }
+                    String detectedText = word.getString("DetectedText");
+                    wordListResult.add(detectedText);
+                }
+            }
+            //鎴戜滑浠巜ordListResult涓绋�:鍚庨潰锛岄渶瑕佹敮浠樿浆杩愯垂鐢�:涔嬮棿鐨勬枃瀛�
+            String content = extractContentFromWordList(wordListResult, "琛岀▼:", "闇�鏀粯杞繍璐圭敤:");
+            log.info("鎻愬彇鍒拌绋�: {}", content);
+            resultMap.put("琛岀▼", content);
+
             log.info("鎵嬪啓浣撹瘑鍒彁鍙栧埌 {} 涓瓧娈�", resultMap.size());
             return resultMap;
             
@@ -306,6 +339,42 @@
         }
     }
 
+    private static String extractContentFromWordList(List<String> wordListResult, String s, String s1) {
+        //鎻愬彇s鍜宻1涔嬮棿鐨勫唴瀹�
+        //濡傛灉word涓彧鏈変竴鎴�-鎴�->锛岀粺涓�澶勭悊鎴�->
+
+
+        int startIndex = -1;
+        int endIndex = -1;
+
+        for (int i = 0; i < wordListResult.size(); i++) {
+            String word = wordListResult.get(i);
+            if (word.contains(s)) {
+                startIndex = i;
+            }
+
+            if (word.contains(s1)) {
+                endIndex = i;
+            }
+        }
+        if (startIndex == -1 || endIndex == -1 || startIndex >= endIndex) {
+            return "";
+        }
+        List<String> w=wordListResult.subList(startIndex + 1, endIndex);
+        Boolean findAle=false;
+        List<String> result=new ArrayList<>();
+       for(String word:w){
+            if (!findAle && (word.equals("-") || word.equals("->") || word.equals("鈫�") || word.equals("涓�") || word.equals("=>")) ){
+                findAle = true;
+                word = word.replace("-", "鈫�")
+                        .replace("涓�", "鈫�")
+                        .replace("=>", "鈫�");
+            }
+            result.add(word);
+        };
+        return String.join("", result);
+    }
+
     /**
      * 韬唤璇佽瘑鍒�
      * @param imagePath 鍥剧墖璺緞
diff --git a/ruoyi-system/src/main/resources/mapper/system/SysTaskMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysTaskMapper.xml
index 4fe1ac6..99faf07 100644
--- a/ruoyi-system/src/main/resources/mapper/system/SysTaskMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/SysTaskMapper.xml
@@ -354,4 +354,33 @@
           and e.patient_phone = #{phone}
           and DATE(t.create_time) = #{createDate}
     </select>
+    
+    <!-- 鏌ヨ杞﹁締鍦ㄦ寚瀹氭椂闂磋寖鍥村唴鐨勪换鍔″垪琛� -->
+    <select id="selectVehicleTasksInTimeRange" parameterType="map" resultMap="SysTaskResult">
+        select t.task_id, t.task_code, t.task_type, t.task_status,
+               t.departure_address, t.destination_address,
+               t.actual_start_time, t.actual_end_time,
+               t.planned_start_time, t.planned_end_time,
+               t.estimated_distance,
+               tv.vehicle_id
+        from sys_task t
+        inner join sys_task_vehicle tv on t.task_id = tv.task_id
+        where tv.vehicle_id = #{vehicleId}
+          and t.del_flag = '0'
+          and t.task_status not in ('CANCELLED')
+          and (
+              <!-- 瀹為檯鏃堕棿鏈夊�兼椂锛屼娇鐢ㄥ疄闄呮椂闂村垽鏂噸鍙� -->
+              (t.actual_start_time is not null and t.actual_end_time is not null 
+               and t.actual_start_time &lt;= #{endTime} and t.actual_end_time &gt;= #{startTime})
+              or
+              <!-- 瀹為檯寮�濮嬫椂闂存湁鍊间絾鏈粨鏉熸椂锛屼娇鐢ㄥ綋鍓嶆椂闂翠綔涓虹粨鏉熸椂闂� -->
+              (t.actual_start_time is not null and t.actual_end_time is null 
+               and t.actual_start_time &lt;= #{endTime})
+              or
+              <!-- 瀹為檯鏃堕棿閮戒负绌烘椂锛屼娇鐢ㄨ鍒掓椂闂村垽鏂噸鍙� -->
+              (t.actual_start_time is null and t.actual_end_time is null
+               and t.planned_start_time &lt;= #{endTime} and t.planned_end_time &gt;= #{startTime})
+          )
+        order by t.actual_start_time, t.planned_start_time
+    </select>
 </mapper>
diff --git a/ruoyi-ui/src/api/task.js b/ruoyi-ui/src/api/task.js
index 8a1ae69..b72da8d 100644
--- a/ruoyi-ui/src/api/task.js
+++ b/ruoyi-ui/src/api/task.js
@@ -284,4 +284,22 @@
     method: 'get',
     params: { taskId }
   })
+}
+
+// ========== 鏃х郴缁熷悓姝ョ浉鍏矨PI ==========
+
+// 鎵嬪姩鍚屾鏈嶅姟鍗曞埌鏃х郴缁�
+export function syncServiceOrder(taskId) {
+  return request({
+    url: '/task/syncServiceOrder/' + taskId,
+    method: 'post'
+  })
+}
+
+// 鎵嬪姩鍚屾璋冨害鍗曞埌鏃х郴缁�
+export function syncDispatchOrder(taskId) {
+  return request({
+    url: '/task/syncDispatchOrder/' + taskId,
+    method: 'post'
+  })
 }
\ No newline at end of file
diff --git a/ruoyi-ui/src/views/task/general/detail.vue b/ruoyi-ui/src/views/task/general/detail.vue
index 036d380..bd5a736 100644
--- a/ruoyi-ui/src/views/task/general/detail.vue
+++ b/ruoyi-ui/src/views/task/general/detail.vue
@@ -74,6 +74,16 @@
             <i class="el-icon-error"></i> 鍚屾澶辫触
           </el-tag>
           <span v-else style="color: #C0C4CC;">--</span>
+          <!-- 鏈悓姝ユ垨鍚屾澶辫触鏃舵樉绀哄悓姝ユ寜閽� -->
+          <el-button
+            v-if="taskDetail.emergencyInfo.syncStatus === 0 || taskDetail.emergencyInfo.syncStatus === 3"
+            type="primary"
+            size="mini"
+            icon="el-icon-refresh"
+            :loading="syncingServiceOrder"
+            @click="syncServiceOrder"
+            style="margin-left: 10px;"
+          >鍚屾鏈嶅姟鍗�</el-button>
         </el-descriptions-item>
         <el-descriptions-item label="鏈嶅姟鍗曞彿">
           <span v-if="taskDetail.emergencyInfo.legacyServiceOrdId">
@@ -109,6 +119,16 @@
             <i class="el-icon-error"></i> 鍚屾澶辫触
           </el-tag>
           <span v-else style="color: #C0C4CC;">--</span>
+          <!-- 鏈悓姝ユ垨鍚屾澶辫触鏃舵樉绀哄悓姝ユ寜閽� -->
+          <el-button
+            v-if="taskDetail.emergencyInfo.dispatchSyncStatus === 0 || taskDetail.emergencyInfo.dispatchSyncStatus === 3"
+            type="primary"
+            size="mini"
+            icon="el-icon-refresh"
+            :loading="syncingDispatchOrder"
+            @click="syncDispatchOrder"
+            style="margin-left: 10px;"
+          >鍚屾璋冨害鍗�</el-button>
         </el-descriptions-item>
         <el-descriptions-item label="璋冨害鍗曞彿">
           <span v-if="taskDetail.emergencyInfo.legacyDispatchOrdId">
@@ -738,7 +758,7 @@
 </template>
 
 <script>
-import { getTask, updateTask, assignTask, changeTaskStatus, uploadAttachment, deleteAttachment, getTaskVehicles, getAvailableVehicles, assignVehiclesToTask, unassignVehicleFromTask, getPaymentInfo } from "@/api/task";
+import { getTask, updateTask, assignTask, changeTaskStatus, uploadAttachment, deleteAttachment, getTaskVehicles, getAvailableVehicles, assignVehiclesToTask, unassignVehicleFromTask, getPaymentInfo, syncServiceOrder, syncDispatchOrder } from "@/api/task";
 import { listUser } from "@/api/system/user";
 import { getToken } from "@/utils/auth";
 
@@ -827,7 +847,10 @@
         category: [
           { required: true, message: "涓氬姟鍒嗙被涓嶈兘涓虹┖", trigger: "change" }
         ]
-      }
+      },
+      // 鍚屾鍔犺浇鐘舵��
+      syncingServiceOrder: false,
+      syncingDispatchOrder: false
     };
   },
   created() {
@@ -1125,6 +1148,36 @@
       if (!fileType) return false;
       const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
       return imageTypes.includes(fileType.toLowerCase());
+    },
+    /** 鎵嬪姩鍚屾鏈嶅姟鍗� */
+    syncServiceOrder() {
+      this.$modal.confirm('鏄惁纭鍚屾鏈嶅姟鍗曞埌鏃х郴缁燂紵').then(() => {
+        this.syncingServiceOrder = true;
+        return syncServiceOrder(this.taskDetail.taskId);
+      }).then(() => {
+        this.$modal.msgSuccess("鏈嶅姟鍗曞悓姝ユ垚鍔�");
+        // 閲嶆柊鍔犺浇浠诲姟璇︽儏
+        this.getDetail();
+      }).catch(() => {
+        // 澶勭悊鍙栨秷鍜岄敊璇�
+      }).finally(() => {
+        this.syncingServiceOrder = false;
+      });
+    },
+    /** 鎵嬪姩鍚屾璋冨害鍗� */
+    syncDispatchOrder() {
+      this.$modal.confirm('鏄惁纭鍚屾璋冨害鍗曞埌鏃х郴缁燂紵').then(() => {
+        this.syncingDispatchOrder = true;
+        return syncDispatchOrder(this.taskDetail.taskId);
+      }).then(() => {
+        this.$modal.msgSuccess("璋冨害鍗曞悓姝ユ垚鍔�");
+        // 閲嶆柊鍔犺浇浠诲姟璇︽儏
+        this.getDetail();
+      }).catch(() => {
+        // 澶勭悊鍙栨秷鍜岄敊璇�
+      }).finally(() => {
+        this.syncingDispatchOrder = false;
+      });
     }
   }
 };

--
Gitblit v1.9.1