From 2f09efc660bf2cc94cbc5291ad25ca06fc9bdadf Mon Sep 17 00:00:00 2001
From: wlzboy <66905212@qq.com>
Date: 星期六, 24 一月 2026 22:03:09 +0800
Subject: [PATCH] feat: 增加OCR测试,车辆

---
 医院信息分词搜索功能说明.md                                                                                   |  274 +
 ruoyi-system/src/main/resources/mapper/system/TbHospDataMapper.xml                                |   24 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleSyncController.java              |  164 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleAlertConfigController.java       |  106 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleAbnormalAlertMapper.java                |   92 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacyTransferSyncServiceImpl.java       |    8 
 ruoyi-system/src/main/java/com/ruoyi/system/controller/OCRController.java                         |  433 +
 ruoyi-system/src/main/resources/mapper/system/DispatchOrdMapper.xml                               |    7 
 ruoyi-system/src/main/resources/mapper/system/VehicleAlertConfigMapper.xml                        |  135 
 医院分词搜索-快速使用指南.md                                                                                  |  432 +
 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskService.java                          |   16 
 ruoyi-ui/src/router/index.js                                                                      |   51 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskStatusPushServiceImpl.java           |   23 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbHospDataMapper.java                          |   10 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacyTransferSyncTask.java                      |    2 
 ruoyi-ui/src/views/system/vehicleAlertConfig/index.vue                                            |  486 +
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/QyWechatServiceImpl.java                 |    8 
 ruoyi-system/src/main/java/com/ruoyi/system/domain/HospitalTokenizerTask.java                     |  143 
 ruoyi-system/src/main/java/com/ruoyi/system/listener/TaskMessageListener.java                     |   15 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskController.java                    |    8 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskStatusSyncServiceImpl.java           |    2 
 ruoyi-system/src/main/java/com/ruoyi/system/utils/AliOCRUtil.java                                 |  457 +
 doc/车辆异常运行监控告警功能说明.md                                                                             |  287 +
 ruoyi-system/src/main/java/com/ruoyi/system/service/IQyWechatService.java                         |   10 
 ruoyi-system/src/main/java/com/ruoyi/system/config/OCRConfig.java                                 |   98 
 ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/VehicleSyncVO.java                          |  100 
 ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleAbnormalAlertService.java             |   97 
 ruoyi-admin/src/main/resources/application.yml                                                    |   15 
 doc/车辆异常运行监控告警-前端部署指南.md                                                                          |  357 +
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleAbnormalAlertController.java     |  149 
 ruoyi-system/src/main/java/com/ruoyi/system/utils/TencentOCRUtil.java                             |  506 ++
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleGpsSegmentMileageServiceImpl.java |    3 
 sql/ocr_module_menu.sql                                                                           |   82 
 sql/vehicle_abnormal_alert.sql                                                                    |  183 
 ruoyi-ui/src/api/system/vehicleAlertConfig.js                                                     |   53 
 ruoyi-system/src/main/java/com/ruoyi/system/controller/NetworkDiagController.java                 |  160 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTaskMapper.java                             |    8 
 ruoyi-admin/src/main/resources/logback.xml                                                        |   11 
 doc/车辆异常运行监控告警-README.md                                                                          |  267 +
 ruoyi-common/src/main/java/com/ruoyi/common/utils/HospitalTokenizerUtil.java                      |  698 ++
 sql/vehicle_abnormal_alert_upgrade.sql                                                            |  150 
 ruoyi-system/src/main/java/com/ruoyi/system/config/BaiduOCRConfig.java                            |   72 
 ruoyi-ui/src/views/system/diag/ocrConnection.vue                                                  |  261 +
 OCR测试功能使用说明.md                                                                                    |  240 
 app/pagesTask/components/HospitalSelector.vue                                                     |   45 
 app/pagesTask/detail.vue                                                                          |   19 
 sql/vehicle_sync_menu.sql                                                                         |   35 
 ruoyi-system/src/main/resources/mapper/system/VehicleAbnormalAlertMapper.xml                      |  178 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleAlertConfigServiceImpl.java       |  109 
 ruoyi-ui/src/views/system/vehicleAlert/index.vue                                                  |  528 ++
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/HospDataController.java                 |  271 +
 ruoyi-ui/src/views/system/ocr/config.vue                                                          |  277 +
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleAbnormalAlertTask.java                    |  547 ++
 ruoyi-system/src/main/java/com/ruoyi/system/utils/BaiduOCRUtil.java                               |  445 +
 ruoyi-ui/src/views/system/vehicleSync/index.vue                                                   |  245 
 doc/车辆异常运行监控告警-实现总结.md                                                                            |  376 +
 sql/tb_hosp_data_add_keywords.sql                                                                 |    9 
 ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleAlertConfig.java                        |  156 
 ruoyi-ui/src/api/system/networkDiag.js                                                            |   41 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/DispatchOrdMapper.java                         |   10 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/DispatchOrdServiceImpl.java              |   12 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbHospDataServiceImpl.java               |   64 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacySystemSyncTask.java                        |    2 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleAbnormalAlertServiceImpl.java     |  173 
 ruoyi-ui/src/api/system/ocr.js                                                                    |   69 
 ruoyi-ui/src/api/system/vehicleAlert.js                                                           |   80 
 ruoyi-ui/src/views/system/hospital/tokenizer.vue                                                  |  418 +
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleAlertConfigMapper.java                  |   71 
 ruoyi-system/src/main/java/com/ruoyi/system/service/HospitalTokenizerAsyncService.java            |  149 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/HospDataSyncServiceImpl.java             |   10 
 sql/hospital_tokenizer_menu.sql                                                                   |   24 
 sql/ocr_test_menu.sql                                                                             |   41 
 app/api/ocr.js                                                                                    |  182 
 doc/车辆异常运行监控告警-菜单配置说明.md                                                                          |  350 +
 ruoyi-system/src/main/java/com/ruoyi/system/domain/TbHospData.java                                |   12 
 ruoyi-ui/src/api/system/vehicle.js                                                                |    2 
 ruoyi-system/src/main/java/com/ruoyi/system/service/IDispatchOrdService.java                      |    9 
 ruoyi-ui/src/views/system/ocr/index.vue                                                           |  386 +
 ruoyi-system/src/main/java/com/ruoyi/system/config/TencentOCRConfig.java                          |   52 
 ruoyi-system/pom.xml                                                                              |   38 
 doc/车辆异常运行监控告警-完整实现总结.md                                                                          |  551 ++
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleGpsSegmentMileageMapper.java            |    7 
 app/pagesTask/create-emergency.vue                                                                | 1349 +++++
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacySystemSyncServiceImpl.java         |    5 
 ruoyi-common/pom.xml                                                                              |    7 
 ruoyi-system/src/main/java/com/ruoyi/system/service/ILegacySystemSyncService.java                 |   10 
 ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleAlertConfigService.java               |   70 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskServiceImpl.java                  |   10 
 ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleAbnormalAlert.java                      |  302 +
 OCR功能完整说明.md                                                                                      |  202 
 OCR使用说明与故障排除指南.md                                                                                 |  157 
 app/api/hospital.js                                                                               |   18 
 ruoyi-ui/src/api/system/vehicleSync.js                                                            |   18 
 doc/车辆异常运行监控告警-快速部署指南.md                                                                          |  262 +
 ruoyi-system/src/main/java/com/ruoyi/system/service/ITbHospDataService.java                       |   16 
 ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml                  |   13 
 96 files changed, 15,040 insertions(+), 95 deletions(-)

diff --git "a/OCR\344\275\277\347\224\250\350\257\264\346\230\216\344\270\216\346\225\205\351\232\234\346\216\222\351\231\244\346\214\207\345\215\227.md" "b/OCR\344\275\277\347\224\250\350\257\264\346\230\216\344\270\216\346\225\205\351\232\234\346\216\222\351\231\244\346\214\207\345\215\227.md"
new file mode 100644
index 0000000..789a208
--- /dev/null
+++ "b/OCR\344\275\277\347\224\250\350\257\264\346\230\216\344\270\216\346\225\205\351\232\234\346\216\222\351\231\244\346\214\207\345\215\227.md"
@@ -0,0 +1,157 @@
+# OCR鍥惧儚璇嗗埆鍔熻兘浣跨敤璇存槑涓庢晠闅滄帓闄ゆ寚鍗�
+
+## 馃摎 鍔熻兘姒傝堪
+
+OCR锛圤ptical Character Recognition锛屽厜瀛﹀瓧绗﹁瘑鍒級鍔熻兘鐢ㄤ簬璇嗗埆鍥剧墖涓殑鏂囧瓧鍐呭锛屾敮鎸佸绉嶈瘑鍒被鍨嬶細
+- 閫氱敤鏂囧瓧璇嗗埆
+- 鍙戠エ璇嗗埆
+- 韬唤璇佽瘑鍒�
+
+## 馃敡 绯荤粺瑕佹眰
+
+### 鏈嶅姟渚濊禆
+- 闃块噷浜慜CR鏈嶅姟锛堥渶瑕佹湁鏁堢殑AccessKey锛�
+- 缃戠粶杩炴帴锛堣闂� `ocr-api.cn-hangzhou.aliyuncs.com:443`锛�
+
+### 鎶�鏈爤
+- 鍚庣锛歋pring Boot + 闃块噷浜慜CR SDK
+- 鍓嶇锛歏ue.js + Element UI
+
+## 鈿欙笍 閰嶇疆璇存槑
+
+### 1. AccessKey閰嶇疆
+鍦� `application.yml` 涓厤缃細
+```yaml
+ocr:
+  accessKeyId: YOUR_ACCESS_KEY_ID
+  accessKeySecret: YOUR_ACCESS_KEY_SECRET
+```
+
+### 2. 缃戠粶閰嶇疆
+纭繚鏈嶅姟鍣ㄨ兘澶熻闂細
+- 鍦板潃锛歚ocr-api.cn-hangzhou.aliyuncs.com`
+- 绔彛锛歚443` (HTTPS)
+- 鍗忚锛歍CP
+
+## 馃殌 浣跨敤鏂规硶
+
+### 1. 璁块棶椤甸潰
+鑿滃崟璺緞锛歚绯荤粺宸ュ叿 > OCR绠$悊 > OCR娴嬭瘯`
+
+### 2. 涓婁紶鍥剧墖
+- 鏀寔鏍煎紡锛欽PG銆丳NG銆丅MP
+- 鏂囦欢澶у皬锛氫笉瓒呰繃4MB
+- 鍥剧墖璐ㄩ噺锛氭竻鏅帮紝鏂囧瓧鏄撹鲸璁�
+
+### 3. 閫夋嫨璇嗗埆绫诲瀷
+- 閫氱敤鏂囧瓧璇嗗埆锛氶�傚悎涓�鑸枃妗�
+- 鍙戠エ璇嗗埆锛氶�傚悎鍙戠エ銆佹敹鎹�
+- 韬唤璇佽瘑鍒細閫傚悎韬唤璇佹鍙嶉潰
+
+## 馃攳 鏁呴殰鎺掗櫎
+
+### 甯歌閿欒鍙婅В鍐虫柟妗�
+
+#### 1. 缃戠粶杩炴帴閿欒
+**閿欒淇℃伅**锛歚code: 415, The image format or content is not supported`
+**鍙兘鍘熷洜**锛�
+- 鍥剧墖鏍煎紡涓嶆敮鎸�
+- 鍥剧墖鍐呭鎹熷潖
+- 鍥剧墖澶ぇ
+
+**瑙e喅鏂规**锛�
+- 妫�鏌ュ浘鐗囨牸寮忔槸鍚︿负JPG/PNG/BMP
+- 楠岃瘉鍥剧墖鏂囦欢鏄惁瀹屾暣
+- 鍘嬬缉鍥剧墖鑷�4MB浠ヤ笅
+
+#### 2. 缃戠粶杩炴帴澶辫触
+**閿欒淇℃伅**锛歚ocr-api.cn-hangzhou.aliyuncs.com`
+**鍙兘鍘熷洜**锛�
+- DNS瑙f瀽澶辫触
+- 闃茬伀澧欓樆姝㈣繛鎺�
+- 缃戠粶绛栫暐闄愬埗
+- 浠g悊閰嶇疆闂
+
+**瑙e喅鏂规**锛�
+1. **DNS闂**锛�
+   - 妫�鏌NS鏈嶅姟鍣ㄩ厤缃�
+   - 灏濊瘯浣跨敤鍏叡DNS锛堝8.8.8.8锛�
+   - 楠岃瘉鍩熷悕瑙f瀽锛歚nslookup ocr-api.cn-hangzhou.aliyuncs.com`
+
+2. **闃茬伀澧欓棶棰�**锛�
+   - 妫�鏌ラ槻鐏鏄惁寮�鏀�443绔彛
+   - 纭鏈嶅姟鍣ㄥ厑璁稿嚭绔橦TTPS璇锋眰
+   - 楠岃瘉瀹夊叏缁勮鍒�
+
+3. **浠g悊闂**锛�
+   - 閰嶇疆绯荤粺浠g悊鍙傛暟锛�
+     ```
+     -Dhttp.proxyHost=proxy.example.com
+     -Dhttp.proxyPort=8080
+     -Dhttps.proxyHost=proxy.example.com
+     -Dhttps.proxyPort=8080
+     ```
+
+#### 3. AccessKey閿欒
+**閿欒淇℃伅**锛氳璇佸け璐ョ浉鍏抽敊璇�
+**瑙e喅鏂规**锛�
+- 妫�鏌ccessKey ID鍜孲ecret鏄惁姝g‘
+- 纭璐︽埛鏈塐CR鏈嶅姟鏉冮檺
+- 楠岃瘉AccessKey鏄惁杩囨湡
+
+### 璇婃柇宸ュ叿
+
+#### 1. 缃戠粶璇婃柇
+璁块棶椤甸潰锛歚绯荤粺宸ュ叿 > OCR绠$悊 > OCR娴嬭瘯`
+鍦ㄨ瘑鍒け璐ユ椂锛岀偣鍑�"缃戠粶璇婃柇"鎸夐挳鏌ョ湅杩炴帴鐘舵�併��
+
+#### 2. 鎵嬪姩娴嬭瘯鍛戒护
+```bash
+# 娴嬭瘯DNS瑙f瀽
+nslookup ocr-api.cn-hangzhou.aliyuncs.com
+
+# 娴嬭瘯绔彛杩為�氭��
+telnet ocr-api.cn-hangzhou.aliyuncs.com 443
+
+# 娴嬭瘯HTTPS杩炴帴
+curl -I https://ocr-api.cn-hangzhou.aliyuncs.com
+```
+
+## 馃洜锔� 绯荤粺缁存姢
+
+### 1. 鏃ュ織鏌ョ湅
+- 鍚庣鏃ュ織锛歚logs/ocr.log`
+- 閿欒鏃ュ織锛氬叧娉� `AliOCRUtil` 绫荤殑鏃ュ織
+
+### 2. 鎬ц兘璋冧紭
+- 杩炴帴瓒呮椂锛氶粯璁�10绉�
+- 璇诲彇瓒呮椂锛氶粯璁�30绉�
+- 鍙�氳繃閰嶇疆璋冩暣瓒呮椂鏃堕棿
+
+### 3. 瀹夊叏娉ㄦ剰浜嬮」
+- AccessKey涓嶈纭紪鐮佸湪浠g爜涓�
+- 瀹氭湡鏇存崲AccessKey
+- 闄愬埗AccessKey鏉冮檺鑼冨洿
+
+## 馃摓 鎶�鏈敮鎸�
+
+濡傞亣鏃犳硶瑙e喅鐨勯棶棰橈紝璇锋彁渚涗互涓嬩俊鎭仈绯绘妧鏈敮鎸侊細
+- 瀹屾暣閿欒鏃ュ織
+- 缃戠粶璇婃柇缁撴灉
+- 绯荤粺鐜淇℃伅
+- 闃茬伀澧�/浠g悊閰嶇疆淇℃伅
+
+## 馃搵 妫�鏌ユ竻鍗�
+
+鍦ㄩ儴缃插拰浣跨敤OCR鍔熻兘鍓嶏紝璇风‘璁わ細
+- [ ] 宸插紑閫氶樋閲屼簯OCR鏈嶅姟
+- [ ] 宸查厤缃湁鏁堢殑AccessKey
+- [ ] 鏈嶅姟鍣ㄥ彲璁块棶浜掕仈缃�
+- [ ] 闃茬伀澧欏紑鏀�443绔彛
+- [ ] DNS瑙f瀽姝e父
+- [ ] 鍥剧墖鏍煎紡鏀寔楠岃瘉
+- [ ] 缃戠粶杩為�氭�ф祴璇曢�氳繃
+
+---
+
+**娉ㄦ剰**锛氭湰鍔熻兘渚濊禆澶栭儴鏈嶅姟锛岀綉缁滅姸鍐靛彲鑳藉奖鍝嶈瘑鍒垚鍔熺巼鍜岄�熷害銆�
\ No newline at end of file
diff --git "a/OCR\345\212\237\350\203\275\345\256\214\346\225\264\350\257\264\346\230\216.md" "b/OCR\345\212\237\350\203\275\345\256\214\346\225\264\350\257\264\346\230\216.md"
new file mode 100644
index 0000000..0064475
--- /dev/null
+++ "b/OCR\345\212\237\350\203\275\345\256\214\346\225\264\350\257\264\346\230\216.md"
@@ -0,0 +1,202 @@
+# OCR鍥惧儚璇嗗埆鍔熻兘瀹屾暣璇存槑
+
+## 馃摎 鍔熻兘姒傝堪
+
+OCR锛圤ptical Character Recognition锛屽厜瀛﹀瓧绗﹁瘑鍒級鍔熻兘鐢ㄤ簬璇嗗埆鍥剧墖涓殑鏂囧瓧鍐呭锛屾敮鎸佸绉嶈瘑鍒被鍨嬶細
+- 閫氱敤鏂囧瓧璇嗗埆
+- 鍙戠エ璇嗗埆
+- 韬唤璇佽瘑鍒�
+- 鎵嬪啓浣撹瘑鍒�
+
+## 馃敡 绯荤粺瑕佹眰
+
+### 鏈嶅姟渚濊禆
+- 闃块噷浜慜CR鏈嶅姟锛堥渶瑕佹湁鏁堢殑AccessKey锛�
+- 缃戠粶杩炴帴锛堣闂� `ocr-api.cn-hangzhou.aliyuncs.com:443`锛�
+
+### 鎶�鏈爤
+- 鍚庣锛歋pring Boot + 闃块噷浜慜CR SDK
+- 鍓嶇锛歏ue.js + Element UI
+
+## 鈿欙笍 閰嶇疆璇存槑
+
+### 1. AccessKey閰嶇疆
+鍦� `application.yml` 涓厤缃細
+```yaml
+ocr:
+  accessKeyId: YOUR_ACCESS_KEY_ID
+  accessKeySecret: YOUR_ACCESS_KEY_SECRET
+```
+
+### 2. 缃戠粶閰嶇疆
+纭繚鏈嶅姟鍣ㄨ兘澶熻闂細
+- 鍦板潃锛歚ocr-api.cn-hangzhou.aliyuncs.com`
+- 绔彛锛歚443` (HTTPS)
+- 鍗忚锛歍CP
+
+## 馃殌 浣跨敤鏂规硶
+
+### 1. 璁块棶椤甸潰
+鑿滃崟璺緞锛歚绯荤粺宸ュ叿 > OCR绠$悊 > OCR娴嬭瘯`
+
+### 2. 涓婁紶鍥剧墖
+- 鏀寔鏍煎紡锛欽PG銆丳NG銆丅MP
+- 鏂囦欢澶у皬锛氫笉瓒呰繃4MB
+- 鍥剧墖璐ㄩ噺锛氭竻鏅帮紝鏂囧瓧鏄撹鲸璁�
+
+### 3. 閫夋嫨璇嗗埆绫诲瀷
+- 閫氱敤鏂囧瓧璇嗗埆锛氶�傚悎涓�鑸枃妗�
+- 鍙戠エ璇嗗埆锛氶�傚悎鍙戠エ銆佹敹鎹�
+- 韬唤璇佽瘑鍒細閫傚悎韬唤璇佹鍙嶉潰
+- 鎵嬪啓浣撹瘑鍒細閫傚悎鎵嬪啓鏂囧瓧璇嗗埆
+
+## 馃洜锔� API鎺ュ彛璇存槑
+
+### 1. 璇嗗埆绫诲瀷鑾峰彇
+- 鎺ュ彛锛歚GET /system/ocr/types`
+- 鍔熻兘锛氳幏鍙栨敮鎸佺殑璇嗗埆绫诲瀷鍒楄〃
+
+### 2. 鏈湴鏂囦欢璇嗗埆
+- 鎺ュ彛锛歚POST /system/ocr/recognize`
+- 鍙傛暟锛歚file`锛堝浘鐗囨枃浠讹級銆乣type`锛堣瘑鍒被鍨嬶級
+- 鍔熻兘锛氫笂浼犲浘鐗囧苟杩涜OCR璇嗗埆
+
+### 3. URL璇嗗埆
+- 鎺ュ彛锛歚GET /system/ocr/recognizeByUrl`
+- 鍙傛暟锛歚imageUrl`锛堝浘鐗嘦RL锛夈�乣type`锛堣瘑鍒被鍨嬶級
+- 鍔熻兘锛氶�氳繃URL杩涜OCR璇嗗埆
+
+### 4. 瀛楁鎻愬彇
+- 鎺ュ彛锛歚POST /system/ocr/extractFields`
+- 鍙傛暟锛歄CR璇嗗埆缁撴灉
+- 鍔熻兘锛氫粠OCR缁撴灉涓彁鍙栧叧閿瓧娈�
+
+### 5. 缃戠粶璇婃柇
+- 鎺ュ彛锛歚GET /system/diag/ocrConnection`
+- 鍔熻兘锛氳瘖鏂璒CR鏈嶅姟杩炴帴鐘舵��
+
+## 馃О 宸ュ叿绫诲姛鑳�
+
+### 1. AliOCRUtil绫�
+鏀寔鐨勮瘑鍒柟娉曪細
+- `recognizeGeneral()` - 閫氱敤鏂囧瓧璇嗗埆
+- `recognizeInvoice()` - 鍙戠エ璇嗗埆
+- `recognizeIdCard()` - 韬唤璇佽瘑鍒�
+- `recognizeHandwriting()` - 鎵嬪啓浣撹瘑鍒�
+
+### 2. 瀛楁鎻愬彇
+- `extractTargetFields()` - 鎻愬彇閲戦銆佹棩鏈熴�佸娉ㄧ瓑鍏抽敭淇℃伅
+
+### 3. 璇嗗埆绫诲瀷鏋氫妇
+- `OcrType.GENERAL` - 閫氱敤鏂囧瓧璇嗗埆
+- `OcrType.INVOICE` - 鍙戠エ璇嗗埆
+- `OcrType.IDCARD` - 韬唤璇佽瘑鍒�
+- `OcrType.HANDWRITING` - 鎵嬪啓浣撹瘑鍒�
+
+## 馃攳 鏁呴殰鎺掗櫎
+
+### 甯歌閿欒鍙婅В鍐虫柟妗�
+
+#### 1. 缃戠粶杩炴帴閿欒
+**閿欒淇℃伅**锛歚code: 415, The image format or content is not supported`
+**鍙兘鍘熷洜**锛�
+- 鍥剧墖鏍煎紡涓嶆敮鎸�
+- 鍥剧墖鍐呭鎹熷潖
+- 鍥剧墖澶ぇ
+
+**瑙e喅鏂规**锛�
+- 妫�鏌ュ浘鐗囨牸寮忔槸鍚︿负JPG/PNG/BMP
+- 楠岃瘉鍥剧墖鏂囦欢鏄惁瀹屾暣
+- 鍘嬬缉鍥剧墖鑷�4MB浠ヤ笅
+
+#### 2. 缃戠粶杩炴帴澶辫触
+**閿欒淇℃伅**锛歚ocr-api.cn-hangzhou.aliyuncs.com`
+**鍙兘鍘熷洜**锛�
+- DNS瑙f瀽澶辫触
+- 闃茬伀澧欓樆姝㈣繛鎺�
+- 缃戠粶绛栫暐闄愬埗
+- 浠g悊閰嶇疆闂
+
+**瑙e喅鏂规**锛�
+1. **DNS闂**锛�
+   - 妫�鏌NS鏈嶅姟鍣ㄩ厤缃�
+   - 灏濊瘯浣跨敤鍏叡DNS锛堝8.8.8.8锛�
+   - 楠岃瘉鍩熷悕瑙f瀽锛歚nslookup ocr-api.cn-hangzhou.aliyuncs.com`
+
+2. **闃茬伀澧欓棶棰�**锛�
+   - 妫�鏌ラ槻鐏鏄惁寮�鏀�443绔彛
+   - 纭鏈嶅姟鍣ㄥ厑璁稿嚭绔橦TTPS璇锋眰
+   - 楠岃瘉瀹夊叏缁勮鍒�
+
+3. **浠g悊闂**锛�
+   - 閰嶇疆绯荤粺浠g悊鍙傛暟锛�
+     ```
+     -Dhttp.proxyHost=proxy.example.com
+     -Dhttp.proxyPort=8080
+     -Dhttps.proxyHost=proxy.example.com
+     -Dhttps.proxyPort=8080
+     ```
+
+#### 3. AccessKey閿欒
+**閿欒淇℃伅**锛氳璇佸け璐ョ浉鍏抽敊璇�
+**瑙e喅鏂规**锛�
+- 妫�鏌ccessKey ID鍜孲ecret鏄惁姝g‘
+- 纭璐︽埛鏈塐CR鏈嶅姟鏉冮檺
+- 楠岃瘉AccessKey鏄惁杩囨湡
+
+### 璇婃柇宸ュ叿
+
+#### 1. 缃戠粶璇婃柇
+璁块棶椤甸潰锛歚绯荤粺宸ュ叿 > OCR绠$悊 > OCR娴嬭瘯`
+鍦ㄨ瘑鍒け璐ユ椂锛岀偣鍑�"缃戠粶璇婃柇"鎸夐挳鏌ョ湅杩炴帴鐘舵�併��
+
+#### 2. 鎵嬪姩娴嬭瘯鍛戒护
+```bash
+# 娴嬭瘯DNS瑙f瀽
+nslookup ocr-api.cn-hangzhou.aliyuncs.com
+
+# 娴嬭瘯绔彛杩為�氭��
+telnet ocr-api.cn-hangzhou.aliyuncs.com 443
+
+# 娴嬭瘯HTTPS杩炴帴
+curl -I https://ocr-api.cn-hangzhou.aliyuncs.com
+```
+
+## 馃洜锔� 绯荤粺缁存姢
+
+### 1. 鏃ュ織鏌ョ湅
+- 鍚庣鏃ュ織锛歚logs/ocr.log`
+- 閿欒鏃ュ織锛氬叧娉� `AliOCRUtil` 绫荤殑鏃ュ織
+
+### 2. 鎬ц兘璋冧紭
+- 杩炴帴瓒呮椂锛氶粯璁�10绉�
+- 璇诲彇瓒呮椂锛氶粯璁�30绉�
+- 鍙�氳繃閰嶇疆璋冩暣瓒呮椂鏃堕棿
+
+### 3. 瀹夊叏娉ㄦ剰浜嬮」
+- AccessKey涓嶈纭紪鐮佸湪浠g爜涓�
+- 瀹氭湡鏇存崲AccessKey
+- 闄愬埗AccessKey鏉冮檺鑼冨洿
+
+## 馃摓 鎶�鏈敮鎸�
+
+濡傞亣鏃犳硶瑙e喅鐨勯棶棰橈紝璇锋彁渚涗互涓嬩俊鎭仈绯绘妧鏈敮鎸侊細
+- 瀹屾暣閿欒鏃ュ織
+- 缃戠粶璇婃柇缁撴灉
+- 绯荤粺鐜淇℃伅
+- 闃茬伀澧�/浠g悊閰嶇疆淇℃伅
+
+## 馃搵 妫�鏌ユ竻鍗�
+
+鍦ㄩ儴缃插拰浣跨敤OCR鍔熻兘鍓嶏紝璇风‘璁わ細
+- [ ] 宸插紑閫氶樋閲屼簯OCR鏈嶅姟
+- [ ] 宸查厤缃湁鏁堢殑AccessKey
+- [ ] 鏈嶅姟鍣ㄥ彲璁块棶浜掕仈缃�
+- [ ] 闃茬伀澧欏紑鏀�443绔彛
+- [ ] DNS瑙f瀽姝e父
+- [ ] 鍥剧墖鏍煎紡鏀寔楠岃瘉
+- [ ] 缃戠粶杩為�氭�ф祴璇曢�氳繃
+
+---
+
+**娉ㄦ剰**锛氭湰鍔熻兘渚濊禆澶栭儴鏈嶅姟锛岀綉缁滅姸鍐靛彲鑳藉奖鍝嶈瘑鍒垚鍔熺巼鍜岄�熷害銆�
\ No newline at end of file
diff --git "a/OCR\346\265\213\350\257\225\345\212\237\350\203\275\344\275\277\347\224\250\350\257\264\346\230\216.md" "b/OCR\346\265\213\350\257\225\345\212\237\350\203\275\344\275\277\347\224\250\350\257\264\346\230\216.md"
new file mode 100644
index 0000000..af479b5
--- /dev/null
+++ "b/OCR\346\265\213\350\257\225\345\212\237\350\203\275\344\275\277\347\224\250\350\257\264\346\230\216.md"
@@ -0,0 +1,240 @@
+# OCR鍥惧儚璇嗗埆娴嬭瘯鍔熻兘浣跨敤璇存槑
+
+## 馃摎 鍔熻兘姒傝堪
+
+OCR鍥惧儚璇嗗埆娴嬭瘯椤甸潰鐢ㄤ簬娴嬭瘯闃块噷浜慜CR鏈嶅姟锛屾敮鎸侀�氱敤鏂囧瓧璇嗗埆銆佸彂绁ㄨ瘑鍒�佽韩浠借瘉璇嗗埆绛夊姛鑳姐��
+
+## 馃幆 宸插疄鐜扮殑鍔熻兘
+
+### 鍚庣閮ㄥ垎
+
+#### 1. OCRController
+**璺緞**: `ruoyi-system/src/main/java/com/ruoyi/system/controller/OCRController.java`
+
+**鎺ュ彛鍒楄〃**:
+- `POST /system/ocr/recognize` - 涓婁紶鍥剧墖杩涜OCR璇嗗埆
+- `GET /system/ocr/recognizeByUrl` - 閫氳繃URL杩涜OCR璇嗗埆
+- `POST /system/ocr/extractFields` - 鎻愬彇OCR缁撴灉涓殑鐩爣瀛楁
+
+#### 2. AliOCRUtil宸ュ叿绫�
+**璺緞**: `ruoyi-system/src/main/java/com/ruoyi/system/utils/AliOCRUtil.java`
+
+**涓昏鏂规硶**:
+- `recognizeTextByFile()` - 鏈湴鏂囦欢璇嗗埆
+- `recognizeTextByUrl()` - URL鍥剧墖璇嗗埆
+- `recognizeInvoice()` - 鍙戠エ璇嗗埆
+- `recognizeIdCard()` - 韬唤璇佽瘑鍒�
+- `recognizeGeneral()` - 閫氱敤鏂囧瓧璇嗗埆
+- `extractTargetFields()` - 瀛楁鎻愬彇
+
+### 鍓嶇閮ㄥ垎
+
+#### 1. OCR娴嬭瘯椤甸潰
+**璺緞**: `ruoyi-ui/src/views/system/ocr/index.vue`
+
+**鍔熻兘鐗规��**:
+- 鎷栨嫿涓婁紶鍥剧墖
+- 鍥剧墖棰勮
+- 璇嗗埆绫诲瀷閫夋嫨锛堥�氱敤/鍙戠エ/韬唤璇侊級
+- 瀹炴椂鏄剧ず璇嗗埆缁撴灉
+- 鑷姩鎻愬彇鍏抽敭瀛楁
+- 鍘熷JSON鏁版嵁鏌ョ湅
+- 涓�閿鍒惰瘑鍒粨鏋�
+
+#### 2. API鎺ュ彛灏佽
+**璺緞**: `ruoyi-ui/src/api/system/ocr.js`
+
+## 馃殌 閮ㄧ讲姝ラ
+
+### 1. 鎵ц鑿滃崟SQL
+
+```bash
+# 鍦∕ySQL涓墽琛�
+mysql -u root -p your_database < sql/ocr_test_menu.sql
+```
+
+鎴栧湪Navicat绛夊伐鍏蜂腑鎵ц `sql/ocr_test_menu.sql` 鏂囦欢
+
+### 2. 閰嶇疆闃块噷浜慉ccessKey
+
+**鏂囦欢**: `ruoyi-admin/src/main/resources/application.yml`
+
+```yaml
+ocr:
+  accessKeyId: YOUR_ACCESS_KEY_ID
+  accessKeySecret: YOUR_ACCESS_KEY_SECRET
+```
+
+**閲嶈**: 璇锋浛鎹负浣犺嚜宸辩殑闃块噷浜慉ccessKey
+
+### 3. 閲嶅惎鍚庣鏈嶅姟
+
+```bash
+cd ruoyi-admin
+mvn spring-boot:run
+```
+
+### 4. 鍒嗛厤鏉冮檺
+
+鐧诲綍鍚庡彴绠$悊绯荤粺锛�
+1. 杩涘叆 **绯荤粺绠$悊 > 瑙掕壊绠$悊**
+2. 閫夋嫨闇�瑕佷娇鐢∣CR鍔熻兘鐨勮鑹�
+3. 鍒嗛厤鏉冮檺锛歚system:ocr:test` 鍜� `system:ocr:recognize`
+
+### 5. 璁块棶椤甸潰
+
+鑿滃崟璺緞锛�**绯荤粺宸ュ叿 > OCR娴嬭瘯**
+
+URL: `http://localhost/system/ocr`
+
+## 馃摉 浣跨敤鎸囧崡
+
+### 鍩烘湰鎿嶄綔娴佺▼
+
+1. **閫夋嫨璇嗗埆绫诲瀷**
+   - 閫氱敤鏂囧瓧璇嗗埆锛氶�傜敤浜庢櫘閫氭枃瀛楀唴瀹�
+   - 鍙戠エ璇嗗埆锛氫笓闂ㄨ瘑鍒彂绁ㄤ俊鎭�
+   - 韬唤璇佽瘑鍒細璇嗗埆韬唤璇佹鍙嶉潰
+
+2. **涓婁紶鍥剧墖**
+   - 鎷栨嫿鍥剧墖鍒颁笂浼犲尯鍩�
+   - 鎴栫偣鍑讳笂浼犲尯鍩熼�夋嫨鏂囦欢
+   - 鏀寔JPG銆丳NG銆丅MP鏍煎紡
+   - 鏂囦欢澶у皬涓嶈秴杩�4MB
+
+3. **寮�濮嬭瘑鍒�**
+   - 鐐瑰嚮"寮�濮嬭瘑鍒�"鎸夐挳
+   - 绛夊緟璇嗗埆瀹屾垚锛堥�氬父1-3绉掞級
+
+4. **鏌ョ湅缁撴灉**
+   - 鎻愬彇瀛楁锛氳嚜鍔ㄦ彁鍙栫殑鍏抽敭淇℃伅锛堥噾棰濄�佹棩鏈熴�佸娉ㄧ瓑锛�
+   - 瀹屾暣璇嗗埆鍐呭锛氭墍鏈夎瘑鍒殑鏂囧瓧
+   - 鍘熷JSON鏁版嵁锛氶樋閲屼簯杩斿洖鐨勫畬鏁存暟鎹�
+
+5. **澶嶅埗缁撴灉**
+   - 鐐瑰嚮"澶嶅埗缁撴灉"鎸夐挳涓�閿鍒惰瘑鍒唴瀹�
+
+### 璇嗗埆绫诲瀷璇存槑
+
+#### 閫氱敤鏂囧瓧璇嗗埆 (General)
+- 閫傜敤鍦烘櫙锛氬悇绫绘枃妗c�佸浘鐗囦腑鐨勬枃瀛�
+- 杩斿洖鍐呭锛氭墍鏈夎瘑鍒殑鏂囧瓧鍙婁綅缃俊鎭�
+
+#### 鍙戠エ璇嗗埆 (Invoice)
+- 閫傜敤鍦烘櫙锛氬鍊肩◣鍙戠エ銆佹櫘閫氬彂绁ㄣ�佹敹鎹瓑
+- 鑷姩鎻愬彇锛氬彂绁ㄥ彿鐮併�侀噾棰濄�佹棩鏈熴�佽喘涔版柟銆侀攢鍞柟绛�
+
+#### 韬唤璇佽瘑鍒� (IdCard)
+- 閫傜敤鍦烘櫙锛氳韩浠借瘉姝i潰鍜屽弽闈�
+- 鑷姩鎻愬彇锛氬鍚嶃�佽韩浠借瘉鍙枫�佸湴鍧�銆佹湁鏁堟湡绛�
+
+## 馃敡 閰嶇疆璇存槑
+
+### OCRConfig閰嶇疆绫�
+**璺緞**: `ruoyi-system/src/main/java/com/ruoyi/system/config/OCRConfig.java`
+
+```java
+@Component
+@ConfigurationProperties(prefix = "ocr")
+public class OCRConfig {
+    private String accessKeyId;
+    private String accessKeySecret;
+    // getter鍜宻etter
+}
+```
+
+### 闃块噷浜慜CR API绔偣
+榛樿浣跨敤鏉窞鑺傜偣锛歚ocr-api.cn-hangzhou.aliyuncs.com`
+
+濡傞渶鏇存敼锛屼慨鏀� `AliOCRUtil.java` 涓殑 `ENDPOINT` 甯搁噺銆�
+
+## 鈿狅笍 娉ㄦ剰浜嬮」
+
+### 1. AccessKey瀹夊叏
+- **涓嶈**灏咥ccessKey鎻愪氦鍒癎it浠撳簱
+- 寤鸿浣跨敤鐜鍙橀噺鎴栭厤缃腑蹇冪鐞�
+- 瀹氭湡鏇存崲AccessKey
+
+### 2. 鏂囦欢澶у皬闄愬埗
+- 鍓嶇闄愬埗锛�4MB
+- 闃块噷浜戦檺鍒讹細鏍规嵁濂楅涓嶅悓锛岄�氬父涓�4-10MB
+- 瓒呰繃闄愬埗浼氬鑷磋瘑鍒け璐�
+
+### 3. 璇嗗埆鍑嗙‘鐜�
+- 鍥剧墖娓呮櫚搴﹀奖鍝嶈瘑鍒噯纭巼
+- 寤鸿浣跨敤300dpi浠ヤ笂鐨勫浘鐗�
+- 閬垮厤妯$硦銆佸�炬枩銆佸弽鍏夌殑鍥剧墖
+
+### 4. 璐圭敤璇存槑
+- 闃块噷浜慜CR鎸夎皟鐢ㄦ鏁版敹璐�
+- 鏂扮敤鎴锋湁鍏嶈垂棰濆害
+- 寤鸿鍦ㄧ敓浜х幆澧冧腑璁剧疆璋冪敤闄愬埗
+
+### 5. 涓存椂鏂囦欢澶勭悊
+- 涓婁紶鐨勫浘鐗囦細淇濆瓨鍒颁复鏃剁洰褰�
+- 璇嗗埆瀹屾垚鍚庤嚜鍔ㄥ垹闄�
+- 涓存椂鐩綍锛歚System.getProperty("java.io.tmpdir")`
+
+## 馃悰 甯歌闂
+
+### 1. 璇嗗埆澶辫触锛欰ccessKey閿欒
+**鍘熷洜**: 閰嶇疆鐨凙ccessKey涓嶆纭�
+
+**瑙e喅**:
+- 妫�鏌� `application.yml` 涓殑閰嶇疆
+- 纭AccessKey ID鍜孲ecret姝g‘
+- 纭璐﹀彿宸插紑閫歄CR鏈嶅姟
+
+### 2. 涓婁紶鍚庢棤鍝嶅簲
+**鍘熷洜**: 鏂囦欢杩囧ぇ鎴栫綉缁滈棶棰�
+
+**瑙e喅**:
+- 妫�鏌ュ浘鐗囧ぇ灏忔槸鍚﹁秴杩�4MB
+- 妫�鏌ョ綉缁滆繛鎺�
+- 鏌ョ湅娴忚鍣ㄦ帶鍒跺彴閿欒淇℃伅
+
+### 3. 璇嗗埆缁撴灉涓虹┖
+**鍘熷洜**: 鍥剧墖璐ㄩ噺闂鎴栦笉鏀寔鐨勫浘鐗囨牸寮�
+
+**瑙e喅**:
+- 浣跨敤娓呮櫚鐨勫浘鐗�
+- 纭繚鍥剧墖鍖呭惈鍙瘑鍒殑鏂囧瓧
+- 灏濊瘯杞崲鍥剧墖鏍煎紡
+
+### 4. 鏉冮檺涓嶈冻
+**鍘熷洜**: 鐢ㄦ埛瑙掕壊鏈垎閰峅CR鏉冮檺
+
+**瑙e喅**:
+- 鑱旂郴绠$悊鍛樺垎閰嶆潈闄�
+- 鎴栧湪瑙掕壊绠$悊涓嬀閫夌浉鍏虫潈闄�
+
+## 馃摑 鎵╁睍寮�鍙�
+
+### 娣诲姞鏂扮殑璇嗗埆绫诲瀷
+
+1. 鍦ㄥ墠绔〉闈㈡坊鍔犻�夐」锛�
+```vue
+<el-option label="钀ヤ笟鎵х収璇嗗埆" value="BusinessLicense" />
+```
+
+2. 鍚庣浼氳嚜鍔ㄦ敮鎸侊紝鏃犻渶淇敼浠g爜
+
+### 鑷畾涔夊瓧娈垫彁鍙�
+
+淇敼 `AliOCRUtil.java` 涓殑 `extractTargetFields()` 鏂规硶锛�
+
+```java
+// 娣诲姞鑷畾涔夋彁鍙栭�昏緫
+if (text.contains("鑷畾涔夊叧閿瓧")) {
+    extracted.put("customField", text);
+}
+```
+
+## 馃摎 鐩稿叧鏂囨。
+
+- [闃块噷浜慜CR瀹樻柟鏂囨。](https://help.aliyun.com/document_detail/442275.html)
+- [RuoYi妗嗘灦鏂囨。](http://doc.ruoyi.vip/)
+
+## 馃摓 鎶�鏈敮鎸�
+
+濡傛湁闂锛岃鑱旂郴鎶�鏈敮鎸佸洟闃熸垨鎻愪氦Issue銆�
diff --git a/app/api/hospital.js b/app/api/hospital.js
index 39e474f..2551dbb 100644
--- a/app/api/hospital.js
+++ b/app/api/hospital.js
@@ -85,3 +85,21 @@
     }
   })
 }
+
+/**
+ * 鍩轰簬鍒嗚瘝鍖归厤鎼滅储鍖婚櫌锛堟柊绠楁硶锛屾櫤鑳藉垎璇�+璇勫垎鎺掑簭锛�
+ * @param {string} searchText 鎼滅储鏂囨湰
+ * @param {number} deptId 閮ㄩ棬ID锛堝彲閫夛紝鐢ㄤ簬鍖哄煙杩囨护锛�
+ * @param {number} limit 杩斿洖缁撴灉鏁伴噺闄愬埗锛堥粯璁�50锛�
+ */
+export function searchHospitalsByKeywords(searchText, deptId, limit = 50) {
+  return request({
+    url: '/system/hospital/searchByKeywords',
+    method: 'get',
+    params: {
+      searchText: searchText,
+      deptId: deptId,
+      pageSize: limit
+    }
+  })
+}
diff --git a/app/api/ocr.js b/app/api/ocr.js
new file mode 100644
index 0000000..e873cee
--- /dev/null
+++ b/app/api/ocr.js
@@ -0,0 +1,182 @@
+import config from '@/config'
+import { getToken } from '@/utils/auth'
+
+const baseUrl = config.baseUrl
+
+/**
+ * OCR璇嗗埆API
+ */
+
+/**
+ * 鍗曞浘OCR璇嗗埆锛堥�氱敤鎺ュ彛锛�
+ * @param {String} filePath 鍥剧墖涓存椂璺緞
+ * @param {String} type 璇嗗埆绫诲瀷锛欻andWriting/IDCard/BankCard/General
+ * @param {String} provider OCR鏈嶅姟鎻愪緵鍟嗭細tencent/baidu
+ * @param {Array} itemNames 闇�瑕佹彁鍙栫殑瀛楁鍚嶇О鏁扮粍锛堟墜鍐欎綋璇嗗埆鏃朵娇鐢級
+ * @returns {Promise}
+ */
+export function recognizeImage(filePath, type = 'HandWriting', provider = 'tencent', itemNames = []) {
+  return new Promise((resolve, reject) => {
+    const token = getToken()
+    
+    // 鏋勫缓formData
+    const formData = {
+      type: type,
+      provider: provider
+    }
+    
+    // 濡傛灉鏈塱temNames锛屾坊鍔犲埌formData
+    if (itemNames && itemNames.length > 0) {
+      itemNames.forEach((itemName, index) => {
+        formData[`itemNames[${index}]`] = itemName
+      })
+    }
+    
+    uni.uploadFile({
+      url: `${baseUrl}/system/ocr/recognize`,
+      filePath: filePath,
+      name: 'file',
+      header: {
+        'Authorization': `Bearer ${token}`
+      },
+      formData: formData,
+      success: (res) => {
+        try {
+          const response = JSON.parse(res.data)
+          if (response.code === 200) {
+            resolve(response)
+          } else {
+            reject(response)
+          }
+        } catch (e) {
+          reject({ msg: '瑙f瀽璇嗗埆缁撴灉澶辫触', error: e })
+        }
+      },
+      fail: (err) => {
+        reject({ msg: 'OCR璇锋眰澶辫触', error: err })
+      }
+    })
+  })
+}
+
+/**
+ * 鑵捐浜戞墜鍐欎綋璇嗗埆锛堝崟鍥撅級
+ * @param {String} filePath 鍥剧墖涓存椂璺緞
+ * @param {Array} itemNames 闇�瑕佹彁鍙栫殑瀛楁鍚嶇О鏁扮粍
+ * @returns {Promise}
+ */
+export function tencentHandwritingRecognize(filePath, itemNames = []) {
+  return new Promise((resolve, reject) => {
+    const token = getToken()
+    
+    // 鏋勫缓formData
+    const formData = {}
+    if (itemNames && itemNames.length > 0) {
+      itemNames.forEach((itemName, index) => {
+        formData[`itemNames[${index}]`] = itemName
+      })
+    }
+    
+    uni.uploadFile({
+      url: `${baseUrl}/system/ocr/tencent/handwriting`,
+      filePath: filePath,
+      name: 'file',
+      header: {
+        'Authorization': `Bearer ${token}`
+      },
+      formData: formData,
+      success: (res) => {
+        try {
+          const response = JSON.parse(res.data)
+          if (response.code === 200) {
+            resolve(response)
+          } else {
+            reject(response)
+          }
+        } catch (e) {
+          reject({ msg: '瑙f瀽璇嗗埆缁撴灉澶辫触', error: e })
+        }
+      },
+      fail: (err) => {
+        reject({ msg: 'OCR璇锋眰澶辫触', error: err })
+      }
+    })
+  })
+}
+
+/**
+ * 鎵归噺OCR璇嗗埆锛堝鍥撅級
+ * @param {Array} filePaths 鍥剧墖涓存椂璺緞鏁扮粍
+ * @param {Array} itemNames 闇�瑕佹彁鍙栫殑瀛楁鍚嶇О鏁扮粍
+ * @returns {Promise} 杩斿洖鍚堝苟鍚庣殑瀛楁Map
+ */
+export function batchRecognizeImages(filePaths, itemNames = []) {
+  if (!filePaths || filePaths.length === 0) {
+    return Promise.reject({ msg: '鍥剧墖鍒楄〃涓嶈兘涓虹┖' })
+  }
+  
+  // 鍒涘缓涓婁紶浠诲姟闃熷垪
+  const uploadPromises = filePaths.map((filePath) => {
+    return tencentHandwritingRecognize(filePath, itemNames)
+  })
+  
+  // 绛夊緟鎵�鏈変笂浼犲畬鎴愬苟鍚堝苟缁撴灉
+  return Promise.all(uploadPromises)
+    .then(results => {
+      // 鍚堝苟鎵�鏈夌粨鏋�
+      const mergedFields = {}
+      let successCount = 0
+      let failCount = 0
+      
+      results.forEach(response => {
+        if (response.code === 200 && response.data && response.data.fields) {
+          const fields = response.data.fields
+          // 鍚堝苟瀛楁锛堝鏋渒ey宸插瓨鍦ㄤ笖涓嶄负绌猴紝涓嶈鐩栵級
+          Object.keys(fields).forEach(key => {
+            if (!mergedFields[key] || mergedFields[key].trim() === '') {
+              mergedFields[key] = fields[key]
+            }
+          })
+          successCount++
+        } else {
+          failCount++
+        }
+      })
+      
+      // 杩囨护缁撴灉锛氬彧杩斿洖itemNames涓寚瀹氱殑瀛楁
+      const filteredFields = {}
+      if (itemNames && itemNames.length > 0) {
+        itemNames.forEach(itemName => {
+          if (mergedFields.hasOwnProperty(itemName)) {
+            filteredFields[itemName] = mergedFields[itemName]
+          }
+        })
+      } else {
+        // 濡傛灉娌℃湁鎸囧畾itemNames锛岃繑鍥炴墍鏈夊瓧娈�
+        Object.assign(filteredFields, mergedFields)
+      }
+      
+      return {
+        success: true,
+        successCount: successCount,
+        failCount: failCount,
+        fields: filteredFields
+      }
+    })
+    .catch(error => {
+      return Promise.reject({
+        success: false,
+        msg: '鎵归噺璇嗗埆澶辫触',
+        error: error
+      })
+    })
+}
+
+/**
+ * 榛樿鐨勮浆杩愬崟瀛楁鍒楄〃
+ */
+export const DEFAULT_TRANSFER_ITEM_NAMES = [
+  "鎮h�呭鍚�", "鎬у埆", "骞撮緞", "韬唤璇佸彿", "璇婃柇", "闇�鏀粯杞繍璐圭敤", 
+  "琛岀▼", "寮�濮嬫椂闂�", "缁撴潫鏃堕棿", "瀹跺睘绛惧悕", "鎮h�呯鍚嶏紙鎵嬪嵃锛�", 
+  "绛惧瓧浜鸿韩浠借瘉鍙风爜", "鏃ユ湡", "鑱旂郴鐢佃瘽", "鏈汉", "绛惧瓧浜轰笌鎮h�呭叧绯�"
+]
diff --git a/app/pagesTask/components/HospitalSelector.vue b/app/pagesTask/components/HospitalSelector.vue
index 3b2dd62..fc57c40 100644
--- a/app/pagesTask/components/HospitalSelector.vue
+++ b/app/pagesTask/components/HospitalSelector.vue
@@ -57,7 +57,7 @@
 </template>
 
 <script>
-import { searchHospitals } from "@/api/hospital"
+import { searchHospitals, searchHospitalsByKeywords } from "@/api/hospital"
 import { searchTianDiTuAddress } from "@/api/map"
 
 export default {
@@ -179,16 +179,38 @@
       }, 300)
     },
     
-    // 鎼滅储鍖婚櫌
+    // 鎼滅储鍖婚櫌锛堟櫤鑳介�夋嫨鎺ュ彛锛�
     searchHospital(keyword) {
-      searchHospitals(keyword, this.deptId).then(response => {
-        this.searchResults = response.data || []
-        this.showResults = true
-      }).catch(error => {
-        // console.error('鎼滅储鍖婚櫌澶辫触:', error)
-        this.searchResults = []
-        // this.showResults = false
-      })
+      // 濡傛灉鍏抽敭璇嶄负绌烘垨鑰呮槸"瀹朵腑"锛屼娇鐢ㄥ師鏉ョ殑鎺ュ彛
+      if (!keyword || keyword.trim() === '' || keyword.trim() === '瀹朵腑') {
+        searchHospitals(keyword || '', this.deptId).then(response => {
+          this.searchResults = response.data || []
+          this.showResults = true
+        }).catch(error => {
+          // console.error('鎼滅储鍖婚櫌澶辫触:', error)
+          this.searchResults = []
+          // this.showResults = false
+        })
+      } else {
+        // 鏈夊叧閿瘝鏃讹紝浣跨敤鏂扮殑鍒嗚瘝鍖归厤鎺ュ彛
+        searchHospitalsByKeywords(keyword, this.deptId).then(response => {
+          // 杞崲鏁版嵁鏍煎紡锛氭彁鍙� hospital 瀵硅薄
+          const rawData = response.data || []
+          this.searchResults = rawData.map(item => {
+            // 濡傛灉鏁版嵁缁撴瀯鏄� {hospital: {...}, matchScore: ...}
+            if (item.hospital) {
+              return item.hospital
+            }
+            // 濡傛灉宸茬粡鏄尰闄㈠璞★紝鐩存帴杩斿洖
+            return item
+          })
+          this.showResults = true
+        }).catch(error => {
+          // console.error('鎼滅储鍖婚櫌澶辫触:', error)
+          this.searchResults = []
+          // this.showResults = false
+        })
+      }
     },
     
     // 杈撳叆妗嗚幏寰楃劍鐐�
@@ -209,8 +231,9 @@
       }
     },
     
-    // 鍔犺浇榛樿鍖婚櫌鍒楄〃
+    // 鍔犺浇榛樿鍖婚櫌鍒楄〃锛堜娇鐢ㄥ師鏉ョ殑鎺ュ彛锛�
     loadDefaultHospitals() {
+      // 浣跨敤鍘熸潵鐨勬帴鍙e姞杞介粯璁ゅ垪琛�
       searchHospitals('', this.deptId).then(response => {
         this.defaultHospitals = response.data || []
         this.searchResults = this.defaultHospitals
diff --git a/app/pagesTask/create-emergency.vue b/app/pagesTask/create-emergency.vue
index d3db251..146f8c0 100644
--- a/app/pagesTask/create-emergency.vue
+++ b/app/pagesTask/create-emergency.vue
@@ -8,8 +8,149 @@
       <view class="smart-parse-btn" @click="showSmartParsePopup">
         <uni-icons type="compose" size="20" color="#007AFF"></uni-icons>
         <text>鏅鸿兘璇嗗埆</text>
+      </view>     
+      <view class="multi-photo-ocr-btn" @click="showMultiPhotoOCRPopup">
+        <uni-icons type="images" size="20" color="#FF6B00"></uni-icons>
+        <text>鎷嶇収璇嗗埆</text>
       </view>
     </view>
+    
+    <!-- 澶氬浘鐗囨媿鐓ц瘑鍒脊绐� -->
+    <uni-popup ref="multiPhotoOCRPopup" type="bottom" :safe-area="true">
+      <view class="photo-ocr-popup">
+        <view class="popup-header">
+          <view class="popup-title">
+            <uni-icons type="images" size="22" color="#FF6B00"></uni-icons>
+            <text>鎷嶇収璇嗗埆 - 鐭ユ儏鍚屾剰涔�</text>
+          </view>
+          <view class="popup-close" @click="closeMultiPhotoOCRPopup">
+            <uni-icons type="closeempty" size="24" color="#333"></uni-icons>
+          </view>
+        </view>
+        
+        <view class="ocr-content">
+          <view class="ocr-tip">
+            <uni-icons type="info" size="18" color="#FF6B00"></uni-icons>
+            <text>璇峰垎鍒笂浼犵煡鎯呭悓鎰忎功鐨勭涓�椤靛拰绗簩椤碉紝绯荤粺灏嗚嚜鍔ㄨ瘑鍒苟濉厖鍒拌〃鍗曚腑</text>
+          </view>
+          
+          <!-- 鐭ユ儏鍚屾剰涔︾涓�椤� -->
+          <view class="page-upload-section first-page">
+            <view class="page-header">
+              <view class="page-title">
+                <view class="page-badge">
+                  <uni-icons type="compose" size="18" color="#52c41a"></uni-icons>
+                  <text class="page-number">1</text>
+                </view>
+                <view class="page-info">
+                  <text class="page-main-title">鐭ユ儏鍚屾剰涔︾涓�椤�</text>
+                  <text class="page-sub-title">鎮h�呭熀鏈俊鎭〉</text>
+                </view>
+              </view>
+              <!-- 鍙湁鏈笂浼犳椂鏄剧ず+鍙� -->
+              <view class="upload-btn green" @click="selectPage1Images" v-if="!page1Image">
+                <uni-icons type="plusempty" size="30" color="#52c41a"></uni-icons>
+              </view>
+            </view>
+            
+            <!-- 璇嗗埆瀛楁鎻愮ず -->
+            <view class="field-hint" v-if="!page1Image">
+              <text>璇嗗埆瀛楁锛氭偅鑰呭鍚嶃�佹�у埆銆佸勾榫勩�佽韩浠借瘉鍙枫�佽瘖鏂�佽浆杩愯垂鐢ㄣ�佽绋嬨�佸紑濮嬫椂闂淬�佺粨鏉熸椂闂淬�佸灞炵鍚�</text>
+            </view>
+            
+            <!-- 鍗曞浘棰勮 -->
+            <view class="single-image-container" v-if="page1Image">
+              <image :src="page1Image" mode="aspectFit" class="preview-image"></image>
+              <view class="delete-btn" @click="deletePage1Image">
+                <uni-icons type="closeempty" size="20" color="#fff"></uni-icons>
+              </view>
+              <view class="image-status">
+                <uni-icons type="checkmarkempty" size="16" color="#fff"></uni-icons>
+                <text>宸蹭笂浼�</text>
+              </view>
+            </view>
+            
+            <!-- 绗竴椤佃瘑鍒粨鏋� -->
+            <view class="recognition-result" v-if="Object.keys(page1Fields).length > 0">
+              <view class="result-title">
+                <uni-icons type="checkmarkempty" size="18" color="#52c41a"></uni-icons>
+                <text>璇嗗埆缁撴灉</text>
+                <text class="result-count">(鍏眥{ Object.keys(page1Fields).length }}涓瓧娈�)</text>
+              </view>
+              <view class="field-list">
+                <view class="field-item" v-for="(value, key) in page1Fields" :key="key">
+                  <text class="field-name">{{ key }}</text>
+                  <text class="field-value">{{ value || '鏈瘑鍒�' }}</text>
+                </view>
+              </view>
+            </view>
+          </view>
+          
+          <!-- 鐭ユ儏鍚屾剰涔︾浜岄〉 -->
+          <view class="page-upload-section second-page">
+            <view class="page-header">
+              <view class="page-title">
+                <view class="page-badge">
+                  <uni-icons type="compose" size="18" color="#FF6B00"></uni-icons>
+                  <text class="page-number">2</text>
+                </view>
+                <view class="page-info">
+                  <text class="page-main-title">鐭ユ儏鍚屾剰涔︾浜岄〉</text>
+                  <text class="page-sub-title">绛惧悕涓庤仈绯讳俊鎭〉</text>
+                </view>
+              </view>
+              <!-- 鍙湁鏈笂浼犳椂鏄剧ず+鍙� -->
+              <view class="upload-btn orange" @click="selectPage2Images" v-if="!page2Image">
+                <uni-icons type="plusempty" size="30" color="#FF6B00"></uni-icons>
+              </view>
+            </view>
+            
+            <!-- 璇嗗埆瀛楁鎻愮ず -->
+            <view class="field-hint" v-if="!page2Image">
+              <text>璇嗗埆瀛楁锛氭偅鑰呯鍚嶃�佺瀛椾汉韬唤璇佸彿銆佺瀛楁棩鏈熴�佽仈绯荤數璇濄�佺瀛椾汉鍏崇郴</text>
+            </view>
+            
+            <!-- 鍗曞浘棰勮 -->
+            <view class="single-image-container" v-if="page2Image">
+              <image :src="page2Image" mode="aspectFit" class="preview-image"></image>
+              <view class="delete-btn" @click="deletePage2Image">
+                <uni-icons type="closeempty" size="20" color="#fff"></uni-icons>
+              </view>
+              <view class="image-status">
+                <uni-icons type="checkmarkempty" size="16" color="#fff"></uni-icons>
+                <text>宸蹭笂浼�</text>
+              </view>
+            </view>
+            
+            <!-- 绗簩椤佃瘑鍒粨鏋� -->
+            <view class="recognition-result" v-if="Object.keys(page2Fields).length > 0">
+              <view class="result-title">
+                <uni-icons type="checkmarkempty" size="18" color="#FF6B00"></uni-icons>
+                <text>璇嗗埆缁撴灉</text>
+                <text class="result-count">(鍏眥{ Object.keys(page2Fields).length }}涓瓧娈�)</text>
+              </view>
+              <view class="field-list">
+                <view class="field-item" v-for="(value, key) in page2Fields" :key="key">
+                  <text class="field-name">{{ key }}</text>
+                  <text class="field-value">{{ value || '鏈瘑鍒�' }}</text>
+                </view>
+              </view>
+            </view>
+          </view>
+        </view>
+        
+        <view class="popup-footer">
+          <button class="cancel-btn" @click="closeMultiPhotoOCRPopup">鍙栨秷</button>
+          <button 
+            class="confirm-btn" 
+            @click="applyOcrResult" 
+            :disabled="Object.keys(page1Fields).length === 0 && Object.keys(page2Fields).length === 0"
+          >
+            搴旂敤鍒拌〃鍗�
+          </button>
+        </view>
+      </view>
+    </uni-popup>
     
     <view class="form-section">
       <view class="form-item">
@@ -259,6 +400,41 @@
         </view>
       </view>
     </uni-popup>
+    
+    <!-- 鎷嶇収璇嗗埆寮圭獥 -->
+    <uni-popup ref="photoOCRPopup" type="bottom" :safe-area="true">
+      <view class="photo-ocr-popup">
+        <view class="popup-header">
+          <view class="popup-title">鎷嶇収璇嗗埆</view>
+          <view class="popup-close" @click="closePhotoOCRPopup">
+            <uni-icons type="closeempty" size="24" color="#333"></uni-icons>
+          </view>
+        </view>
+        
+        <view class="ocr-content">
+          <view class="ocr-tip">
+            <uni-icons type="info" size="18" color="#007AFF"></uni-icons>
+            <text>鎷嶇収鎴栭�夋嫨鍥剧墖锛岃嚜鍔ㄨ瘑鍒浆杩愬崟淇℃伅</text>
+          </view>
+          
+          <view class="image-preview" v-if="ocrImage">
+            <image :src="ocrImage" mode="aspectFit" style="width: 100%; height: 300rpx; border-radius: 10rpx;"></image>
+          </view>
+          
+          <view class="ocr-actions">
+            <button class="select-btn" @click="selectImage">閫夋嫨鍥剧墖</button>
+            <button class="capture-btn" @click="captureImage">鎷嶇収</button>
+          </view>
+        </view>
+        
+        <view class="popup-footer">
+          <button class="cancel-btn" @click="closePhotoOCRPopup">鍙栨秷</button>
+          <button class="confirm-btn" @click="performOCR" :disabled="ocrLoading">
+            {{ ocrLoading ? '璇嗗埆涓�...' : '寮�濮嬭瘑鍒�' }}
+          </button>
+        </view>
+      </view>
+    </uni-popup>
   </scroll-view>
 </template>
 
@@ -268,13 +444,16 @@
 import uniPopup from '@/uni_modules/uni-popup/components/uni-popup/uni-popup.vue'
 import { addTask, checkTaskDuplicate } from "@/api/task"
 import { listAvailableVehicles, getUserBoundVehicle } from "@/api/vehicle"
-import { searchHospitals, searchHospitalsByDeptRegion } from "@/api/hospital"
+import { searchHospitals, searchHospitalsByDeptRegion, searchHospitalsByKeywords } from "@/api/hospital"
 import DepartureSelector from './components/DepartureSelector.vue'
 import { calculateTianDiTuDistance } from "@/api/map"
 import { listBranchUsers } from "@/api/system/user"
 import { searchIcd10 } from "@/api/icd10"
 import { calculateTransferPrice } from "@/api/price"
 import { checkVehicleActiveTasks } from "@/api/task"
+import { recognizeImage, batchRecognizeImages, DEFAULT_TRANSFER_ITEM_NAMES } from "@/api/ocr"
+import config from '@/config'
+import { getToken } from '@/utils/auth'
 
 import { getDicts } from "@/api/dict"
 import { getServiceOrdAreaTypes, getServiceOrderTypes } from "@/api/dictionary"
@@ -357,7 +536,21 @@
       loading: false,
       // 鏅鸿兘璇嗗埆鐩稿叧
       rawText: '',
-      parseLoading: false
+      parseLoading: false,
+      // 鎷嶇収璇嗗埆鐩稿叧
+      ocrImage: '',
+      ocrLoading: false,
+      // 澶氬浘鐗囨媿鐓ц瘑鍒浉鍏�
+      multiOcrImages: [],
+      multiOcrLoading: false,
+      // 鍒嗛〉OCR璇嗗埆鐩稿叧
+      currentOcrPage: 1, // 褰撳墠涓婁紶鐨勯〉鐮侊細1=绗竴椤碉紝2=绗簩椤�
+      page1Image: '', // 绗竴椤靛浘鐗囷紙鍗曞浘锛�
+      page2Image: '', // 绗簩椤靛浘鐗囷紙鍗曞浘锛�
+      page1Fields: {}, // 绗竴椤佃瘑鍒粨鏋�
+      page2Fields: {}, // 绗簩椤佃瘑鍒粨鏋�
+      // 闄勪欢涓存椂瀛樺偍锛圤CR鍥剧墖锛�
+      pendingAttachments: [] // 寰呬笂浼犵殑闄勪欢鍒楄〃 [{ filePath: '', category: '1' }]
     }
   },
   computed: {
@@ -957,25 +1150,93 @@
         
         addTask(submitData).then(response => {
           this.loading = false
-          this.$modal.showToast('浠诲姟鍒涘缓鎴愬姛')
           
-          // 寤惰繜璺宠浆锛岃鐢ㄦ埛鐪嬪埌鎴愬姛鎻愮ず
-          setTimeout(() => {
-            // 璺宠浆鍒颁换鍔″垪琛ㄥ苟瑙﹀彂鍒锋柊
-            uni.switchTab({
-              url: '/pages/task/index',
-              success: () => {
-                // 浣跨敤浜嬩欢鎬荤嚎閫氱煡浠诲姟鍒楄〃椤甸潰鍒锋柊
-                uni.$emit('refreshTaskList')
-              }
+          // 鑾峰彇鍒涘缓鐨勪换鍔D
+          const taskId = response.taskId || (response.data && response.data.taskId) || null
+          console.log('浠诲姟鍒涘缓鎴愬姛锛宼askId:', taskId)
+          
+          // 濡傛灉鏈夊緟涓婁紶鐨勯檮浠讹紙OCR鍥剧墖锛夛紝鍏堜笂浼犻檮浠�
+          if (taskId && this.pendingAttachments.length > 0) {
+            this.uploadPendingAttachments(taskId).then(() => {
+              this.$modal.showToast('浠诲姟鍒涘缓鎴愬姛锛岄檮浠跺凡涓婁紶')
+              this.navigateToTaskList()
+            }).catch(error => {
+              console.error('闄勪欢涓婁紶澶辫触:', error)
+              this.$modal.showToast('浠诲姟鍒涘缓鎴愬姛锛屼絾闄勪欢涓婁紶澶辫触')
+              this.navigateToTaskList()
             })
-          }, 1000)
+          } else {
+            this.$modal.showToast('浠诲姟鍒涘缓鎴愬姛')
+            this.navigateToTaskList()
+          }
         }).catch(error => {
           this.loading = false
           console.error('浠诲姟鍒涘缓澶辫触:', error)
           this.$modal.showToast('浠诲姟鍒涘缓澶辫触锛岃閲嶈瘯')
         })
       }).catch(() => {})
+    },
+    
+    // 涓婁紶寰呬笂浼犵殑闄勪欢锛圤CR鍥剧墖锛�
+    uploadPendingAttachments(taskId) {
+      console.log('寮�濮嬩笂浼犻檮浠讹紝taskId:', taskId, '闄勪欢鏁伴噺:', this.pendingAttachments.length)
+      
+      // 浣跨敤 Promise.all 骞跺彂涓婁紶鎵�鏈夐檮浠�
+      const uploadPromises = this.pendingAttachments.map(attachment => {
+        return this.uploadSingleAttachment(taskId, attachment)
+      })
+      
+      return Promise.all(uploadPromises)
+    },
+    
+    // 涓婁紶鍗曚釜闄勪欢
+    uploadSingleAttachment(taskId, attachment) {
+      return new Promise((resolve, reject) => {
+        uni.uploadFile({
+          url: config.baseUrl + '/task/attachment/upload/' + taskId,
+          filePath: attachment.filePath,
+          name: 'file',
+          formData: {
+            'category': attachment.category
+          },
+          header: {
+            'Authorization': 'Bearer ' + getToken()
+          },
+          success: function(uploadRes) {
+            if (uploadRes.statusCode === 200) {
+              const result = JSON.parse(uploadRes.data)
+              if (result.code === 200) {
+                console.log('闄勪欢涓婁紶鎴愬姛:', attachment.description)
+                resolve(result)
+              } else {
+                console.error('闄勪欢涓婁紶澶辫触:', attachment.description, result.msg)
+                reject(result)
+              }
+            } else {
+              console.error('闄勪欢涓婁紶澶辫触:', attachment.description, uploadRes)
+              reject(uploadRes)
+            }
+          },
+          fail: function(err) {
+            console.error('闄勪欢涓婁紶澶辫触:', attachment.description, err)
+            reject(err)
+          }
+        })
+      })
+    },
+    
+    // 璺宠浆鍒颁换鍔″垪琛�
+    navigateToTaskList() {
+      setTimeout(() => {
+        // 璺宠浆鍒颁换鍔″垪琛ㄥ苟瑙﹀彂鍒锋柊
+        uni.switchTab({
+          url: '/pages/task/index',
+          success: () => {
+            // 浣跨敤浜嬩欢鎬荤嚎閫氱煡浠诲姟鍒楄〃椤甸潰鍒锋柊
+            uni.$emit('refreshTaskList')
+          }
+        })
+      }, 1000)
     },
     
    goBack() {
@@ -1280,7 +1541,7 @@
     findHospitalByName(name, type, restrictRegion = true) {
       if (!name) return Promise.resolve(null)
       const normalized = name.trim()
-
+    
       // 鐗规畩澶勭悊"瀹朵腑"
       if (normalized === '瀹朵腑') {
         // 鏌ヨ鍖婚櫌搴撲腑鐨�"瀹朵腑"璁板綍
@@ -1288,7 +1549,7 @@
         const queryPromise = restrictRegion && deptId
           ? searchHospitalsByDeptRegion('瀹朵腑', deptId, 50)
           : searchHospitals('瀹朵腑', null, 50)
-        
+            
         return queryPromise.then(res => {
           const list = res.data || []
           // 鏌ユ壘鍚嶇О涓�"瀹朵腑"鐨勫尰闄㈣褰�
@@ -1313,20 +1574,23 @@
           }
         })
       }
-
-      // restrictRegion=false 鏃惰蛋鍏ㄩ噺鏌ヨ锛泃rue 涓旀湁 deptId 鏃惰蛋鍖哄煙鎺ュ彛
+    
+      // OCR璇嗗埆鍚庣殑鍖婚櫌鍚嶇О锛屼娇鐢ㄦ柊鐨勫垎璇嶅尮閰嶆帴鍙�
       const deptId = this.selectedOrganizationId || null
-      const queryPromise = (restrictRegion && deptId)
-        ? searchHospitalsByDeptRegion(normalized, deptId, 50)
-        : searchHospitals(normalized, null, 50)
-
-      return queryPromise.then(res => {
-        const list = res.data || []
-        if (!list.length) return null
-        
-        // 鑷姩閫夋嫨绗竴涓潪"瀹朵腑"鐨勫尯闄紝濡傛灉鍏ㄦ槸"瀹朵腑"鍒欓�夌涓�涓�
-        const best = this.pickBestHospitalMatch(list, normalized)
-        return best || null
+                
+      return searchHospitalsByKeywords(normalized, deptId, 50).then(res => {
+        const rawData = res.data || []
+        if (!rawData.length) return null
+              
+        // 鎻愬彇 hospital 瀵硅薄锛堟帴鍙h繑鍥炴牸寮忥細{hospital: {...}, matchScore: ...}锛�
+        const firstItem = rawData[0]
+        const firstHospital = firstItem.hospital || firstItem
+              
+        console.log(`OCR璇嗗埆鍖婚櫌"${normalized}"锛岃嚜鍔ㄩ�変腑锛�${firstHospital.hospName}锛堝尮閰嶅垎鏁帮細${firstItem.matchScore || 'N/A'}锛塦)
+        return firstHospital
+      }).catch(error => {
+        console.error(`鎼滅储鍖婚櫌"${normalized}"澶辫触:`, error)
+        return null
       })
     },
 
@@ -1424,6 +1688,496 @@
           console.error('璺濈璁$畻澶辫触:', error)
           this.$modal.showToast('璺濈璁$畻澶辫触锛岃鎵嬪姩杈撳叆')
         })
+    },
+    
+    // ==================== 鎷嶇収璇嗗埆鐩稿叧鏂规硶 ====================
+    
+    // 鏄剧ず鎷嶇収璇嗗埆寮圭獥
+    showPhotoOCRPopup() {
+      this.ocrImage = ''
+      this.$refs.photoOCRPopup.open()
+    },
+    
+    // 鏄剧ず澶氬浘鎷嶇収璇嗗埆寮圭獥
+    showMultiPhotoOCRPopup() {
+      this.page1Image = ''
+      this.page2Image = ''
+      this.page1Fields = {}
+      this.page2Fields = {}
+      this.$refs.multiPhotoOCRPopup.open()
+    },
+    
+    // 鍏抽棴澶氬浘鎷嶇収璇嗗埆寮圭獥
+    closeMultiPhotoOCRPopup() {
+      this.$refs.multiPhotoOCRPopup.close()
+    },
+    
+    // 閫夋嫨绗竴椤靛浘鐗�
+    selectPage1Images() {
+      uni.showActionSheet({
+        itemList: ['浠庣浉鍐岄�夋嫨', '鎷嶇収'],
+        success: (res) => {
+          if (res.tapIndex === 0) {
+            this.chooseImageForPage(1, 'album')
+          } else if (res.tapIndex === 1) {
+            this.chooseImageForPage(1, 'camera')
+          }
+        }
+      })
+    },
+    
+    // 閫夋嫨绗簩椤靛浘鐗�
+    selectPage2Images() {
+      uni.showActionSheet({
+        itemList: ['浠庣浉鍐岄�夋嫨', '鎷嶇収'],
+        success: (res) => {
+          if (res.tapIndex === 0) {
+            this.chooseImageForPage(2, 'album')
+          } else if (res.tapIndex === 1) {
+            this.chooseImageForPage(2, 'camera')
+          }
+        }
+      })
+    },
+    
+    // 鍒犻櫎绗竴椤靛浘鐗�
+    deletePage1Image() {
+      this.page1Image = ''
+      this.page1Fields = {}
+    },
+    
+    // 鍒犻櫎绗簩椤靛浘鐗�
+    deletePage2Image() {
+      this.page2Image = ''
+      this.page2Fields = {}
+    },
+    
+    // 涓烘寚瀹氶〉鐮侀�夋嫨鍥剧墖锛堝崟鍥撅級
+    chooseImageForPage(page, sourceType) {
+      uni.chooseImage({
+        count: 1, // 鍙�夋嫨涓�寮犲浘鐗�
+        sizeType: ['compressed'],
+        sourceType: [sourceType],
+        success: (res) => {
+          const imagePath = res.tempFilePaths[0]
+          
+          if (page === 1) {
+            this.page1Image = imagePath
+          } else {
+            this.page2Image = imagePath
+          }
+          
+          // 閫夋嫨瀹屽浘鐗囧悗锛岀珛鍗宠繘琛孫CR璇嗗埆
+          this.$modal.showToast(`宸查�夋嫨鍥剧墖锛屽紑濮嬭瘑鍒�...`)
+          this.recognizeSinglePage(page)
+        },
+        fail: (err) => {
+          console.error('閫夋嫨鍥剧墖澶辫触:', err)
+          this.$modal.showToast('閫夋嫨鍥剧墖澶辫触')
+        }
+      })
+    },
+    
+    // 閫夋嫨鍥剧墖
+    selectImage() {
+      uni.chooseImage({
+        count: 1,
+        sizeType: ['compressed'],
+        sourceType: ['album'],
+        success: (res) => {
+          this.ocrImage = res.tempFilePaths[0]
+        },
+        fail: (err) => {
+          console.error('閫夋嫨鍥剧墖澶辫触:', err)
+          this.$modal.showToast('閫夋嫨鍥剧墖澶辫触')
+        }
+      })
+    },
+    
+    // 鎷嶇収
+    captureImage() {
+      uni.chooseImage({
+        count: 1,
+        sizeType: ['compressed'],
+        sourceType: ['camera'],
+        success: (res) => {
+          this.ocrImage = res.tempFilePaths[0]
+        },
+        fail: (err) => {
+          console.error('鎷嶇収澶辫触:', err)
+          this.$modal.showToast('鎷嶇収澶辫触')
+        }
+      })
+    },
+    
+    // 搴旂敤OCR璇嗗埆缁撴灉鍒拌〃鍗�
+    applyOcrResult() {
+      // 鍚堝苟涓ら〉鐨勮瘑鍒粨鏋�
+      const mergedFields = { ...this.page1Fields, ...this.page2Fields }
+      
+      // 澶勭悊鍚堝苟鍚庣殑璇嗗埆缁撴灉
+      this.processMultiOCRResult(mergedFields)
+      
+      // 淇濆瓨OCR鍥剧墖鍒板緟涓婁紶闄勪欢鍒楄〃锛堢煡鎯呭悓鎰忎功锛屽垎绫讳负'1'锛�
+      this.pendingAttachments = []
+      if (this.page1Image) {
+        this.pendingAttachments.push({
+          filePath: this.page1Image,
+          category: '1', // 鐭ユ儏鍚屾剰涔�
+          description: '鐭ユ儏鍚屾剰涔︾涓�椤�'
+        })
+      }
+      if (this.page2Image) {
+        this.pendingAttachments.push({
+          filePath: this.page2Image,
+          category: '1', // 鐭ユ儏鍚屾剰涔�
+          description: '鐭ユ儏鍚屾剰涔︾浜岄〉'
+        })
+      }
+      
+      console.log('淇濆瓨寰呬笂浼犻檮浠�:', this.pendingAttachments)
+      
+      // 鍏抽棴寮圭獥
+      this.closeMultiPhotoOCRPopup()
+      
+      // 娓呯┖鍥剧墖鍜岀粨鏋滐紙浣嗕繚鐣� pendingAttachments锛�
+      this.page1Image = ''
+      this.page2Image = ''
+      this.page1Fields = {}
+      this.page2Fields = {}
+      
+      this.$modal.showToast('宸插簲鐢ㄥ埌琛ㄥ崟锛岀煡鎯呭悓鎰忎功灏嗗湪浠诲姟鍒涘缓鍚庝笂浼�')
+    },
+    
+    // 璇嗗埆鍗曚釜椤电爜鐨勫浘鐗囷紙閫夋嫨鍚庣珛鍗宠瘑鍒級
+    recognizeSinglePage(page) {
+      const image = page === 1 ? this.page1Image : this.page2Image
+      
+      if (!image) {
+        return
+      }
+      
+      // 鏄剧ず鍔犺浇鎻愮ず
+      uni.showLoading({
+        title: `璇嗗埆绗�${page}椤�...`
+      })
+      
+      // 绗竴椤电殑itemNames
+      const page1ItemNames = [
+        "鎮h�呭鍚�", "鎬у埆", "骞撮緞", "韬唤璇佸彿", "璇婃柇", 
+        "闇�鏀粯杞繍璐圭敤", "琛岀▼", "寮�濮嬫椂闂�", "缁撴潫鏃堕棿", "瀹跺睘绛惧悕"
+      ]
+      
+      // 绗簩椤电殑itemNames
+      const page2ItemNames = [
+        "鎮h�呯鍚嶏紙鎵嬪嵃锛�", "绛惧瓧浜鸿韩浠借瘉鍙风爜", "绛惧瓧浜鸿韩浠借瘉鍙�", "鏃ユ湡", 
+        "鑱旂郴鐢佃瘽", "鏈汉", "绛惧瓧浜轰笌鎮h�呭叧绯�"
+      ]
+      
+      const itemNames = page === 1 ? page1ItemNames : page2ItemNames
+      
+      // 璋冪敤鎵归噺OCR API锛堜紶鍏ュ崟涓浘鐗囷級
+      batchRecognizeImages([image], itemNames)
+        .then(result => {
+          uni.hideLoading()
+          
+          if (result.success && result.successCount > 0) {
+            // 淇濆瓨璇嗗埆缁撴灉
+            if (page === 1) {
+              this.page1Fields = result.fields
+            } else {
+              this.page2Fields = result.fields
+            }
+            
+            console.log(`绗�${page}椤佃瘑鍒粨鏋�:`, result.fields)
+            this.$modal.showToast(`绗�${page}椤佃瘑鍒垚鍔焋)
+          } else {
+            this.$modal.showToast(`绗�${page}椤佃瘑鍒け璐)
+          }
+        })
+        .catch(error => {
+          uni.hideLoading()
+          console.error(`绗�${page}椤礝CR璇嗗埆澶辫触:`, error)
+          this.$modal.showToast(error.msg || `绗�${page}椤佃瘑鍒け璐)
+        })
+    },
+    
+    // 澶勭悊澶氬浘OCR璇嗗埆缁撴灉
+    processMultiOCRResult(fields) {
+      console.log('澶氬浘OCR璇嗗埆缁撴灉:', fields)
+      
+      // 鎻愬彇鎮h�呭鍚�
+      if (fields['鎮h�呭鍚�']) {
+        this.taskForm.patient.name = fields['鎮h�呭鍚�'].trim()
+      }
+      
+      // 鎻愬彇鑱旂郴浜猴紙浼樺厛鎮h�呯鍚嶏紝鍏舵瀹跺睘绛惧悕锛屾渶鍚庢湰浜猴級
+      if (fields['鎮h�呯鍚嶏紙鎵嬪嵃锛�']) {
+        this.taskForm.patient.contact = fields['鎮h�呯鍚嶏紙鎵嬪嵃锛�'].trim()
+      } else if (fields['瀹跺睘绛惧悕']) {
+        this.taskForm.patient.contact = fields['瀹跺睘绛惧悕'].trim()
+      } else if (fields['鏈汉']) {
+        this.taskForm.patient.contact = fields['鏈汉'].trim()
+      }
+      
+      // 鎻愬彇鎬у埆
+      if (fields['鎬у埆']) {
+        const gender = fields['鎬у埆'].trim()
+        if (gender.includes('鐢�')) {
+          this.taskForm.patient.gender = 'male'
+        } else if (gender.includes('濂�')) {
+          this.taskForm.patient.gender = 'female'
+        }
+      }
+      
+      // 鎻愬彇韬唤璇佸彿锛堜紭鍏堣韩浠借瘉鍙凤紝鍏舵绛惧瓧浜鸿韩浠借瘉鍙风爜锛�
+      const patientIdCard = fields['韬唤璇佸彿'] || fields['鎮h�呰韩浠借瘉鍙�']
+      const signerIdCard = fields['绛惧瓧浜鸿韩浠借瘉鍙风爜'] || fields['绛惧瓧浜鸿韩浠借瘉鍙�']
+      
+      if (patientIdCard) {
+        this.taskForm.patient.idCard = patientIdCard.trim()
+      } else if (signerIdCard) {
+        this.taskForm.patient.idCard = signerIdCard.trim()
+      }
+      
+      // 鎻愬彇鏃ユ湡锛堣浆杩愭椂闂达級
+      if (fields['鏃ユ湡']) {
+        const dateString = fields['鏃ユ湡'].trim()
+        const dateFormatted = this.formatDateString(dateString)
+        if (dateFormatted) {
+          this.taskForm.transferTime = dateFormatted
+        }
+      }
+      
+      // 鎻愬彇鑱旂郴鐢佃瘽
+      if (fields['鑱旂郴鐢佃瘽']) {
+        this.taskForm.patient.phone = fields['鑱旂郴鐢佃瘽'].trim()
+      }
+      
+      // 鎻愬彇闇�鏀粯杞繍璐圭敤锛堟垚浜や环锛�
+      if (fields['闇�鏀粯杞繍璐圭敤']) {
+        const priceText = fields['闇�鏀粯杞繍璐圭敤'].trim()
+        const priceNumber = priceText.match(/\d+(?:\.\d{1,2})?/)
+        if (priceNumber) {
+          this.taskForm.price = parseFloat(priceNumber[0]).toFixed(2)
+        }
+      }
+      
+      // 鎻愬彇璇婃柇锛堢梾鎯咃級
+      if (fields['璇婃柇']) {
+        this.taskForm.patient.condition = fields['璇婃柇'].trim()
+      }
+      
+      // 鎻愬彇琛岀▼锛堣浆鍑哄尰闄㈠拰杞叆鍖婚櫌锛�
+      if (fields['琛岀▼']) {
+        const route = fields['琛岀▼'].trim()
+        // 鎸�"-"鎴�"鈥�"鍒嗗壊琛岀▼
+        const hospitals = route.split(/[-鈥擼/).map(h => h.trim())
+        if (hospitals.length >= 2) {
+          // 绗竴涓槸杞嚭鍖婚櫌
+          this.taskForm.hospitalOut.name = hospitals[0]
+          // 绗簩涓槸杞叆鍖婚櫌
+          this.taskForm.hospitalIn.name = hospitals[1]
+          
+          // 灏濊瘯浠庡尰闄㈠簱涓尮閰嶅苟琛ュ叏鍦板潃
+          Promise.all([
+            this.findHospitalByName(hospitals[0], 'out', false),
+            this.findHospitalByName(hospitals[1], 'in', false)
+          ]).then(([outHosp, inHosp]) => {
+            if (outHosp) {
+              this.taskForm.hospitalOut.id = outHosp.hospId
+              this.taskForm.hospitalOut.name = outHosp.hospName
+              if (outHosp.hospName !== '瀹朵腑') {
+                this.taskForm.hospitalOut.address = this.buildFullAddress(outHosp)
+                this.taskForm.hospitalOut.city = outHosp.hopsCity || ''
+              }
+            }
+            if (inHosp) {
+              this.taskForm.hospitalIn.id = inHosp.hospId
+              this.taskForm.hospitalIn.name = inHosp.hospName
+              if (inHosp.hospName !== '瀹朵腑') {
+                this.taskForm.hospitalIn.address = this.buildFullAddress(inHosp)
+                this.taskForm.hospitalIn.city = inHosp.hopsCity || ''
+              }
+            }
+            
+            // 濡傛灉涓や釜鍖婚櫌鍦板潃閮芥湁锛岃嚜鍔ㄨ绠楄窛绂�
+            if (this.taskForm.hospitalOut.address && this.taskForm.hospitalIn.address &&
+                this.taskForm.hospitalOut.name !== '瀹朵腑' && this.taskForm.hospitalIn.name !== '瀹朵腑') {
+              this.calculateHospitalDistance()
+            }
+          })
+        }
+      }
+      
+      console.log('澶氬浘OCR缁撴灉澶勭悊瀹屾垚锛岃〃鍗曟暟鎹洿鏂�')
+    },
+    
+    // 鎵цOCR璇嗗埆
+    performOCR() {
+      if (!this.ocrImage) {
+        this.$modal.showToast('璇峰厛閫夋嫨鎴栨媿鎽勫浘鐗�')
+        return
+      }
+      
+      this.ocrLoading = true
+      
+      // 浣跨敤OCR API杩涜璇嗗埆
+      recognizeImage(this.ocrImage, 'HandWriting', 'tencent', DEFAULT_TRANSFER_ITEM_NAMES)
+        .then(response => {
+          const ocrResult = response.data.ocrResult
+          this.processOCRResult(ocrResult)
+          this.$modal.showToast('OCR璇嗗埆鎴愬姛')
+        })
+        .catch(error => {
+          console.error('OCR璇嗗埆澶辫触:', error)
+          this.$modal.showToast(`OCR璇嗗埆澶辫触: ${error.msg || '鏈煡閿欒'}`)
+        })
+        .finally(() => {
+          this.ocrLoading = false
+          this.closePhotoOCRPopup()
+        })
+    },
+    
+    // 澶勭悊OCR璇嗗埆缁撴灉
+    processOCRResult(ocrResult) {
+      if (!ocrResult || !ocrResult.content) {
+        console.log('OCR璇嗗埆缁撴灉涓虹┖')
+        return
+      }
+      
+      const content = ocrResult.content
+      console.log('OCR璇嗗埆鍐呭:', content)
+      
+      // 鎻愬彇鎮h�呭鍚�
+      const patientNameMatch = content.match(/鎮h�呭鍚峓锛�:]?\s*([^\n锛�,銆傦紱;]+)/)
+      if (patientNameMatch && patientNameMatch[1]) {
+        this.taskForm.patient.name = patientNameMatch[1].trim()
+      }
+      
+      // 鎻愬彇鎬у埆
+      const genderMatch = content.match(/鎬у埆[锛�:]?\s*([^\n锛�,銆傦紱;]+)/)
+      if (genderMatch && genderMatch[1]) {
+        const gender = genderMatch[1].trim()
+        if (gender.includes('鐢�')) {
+          this.taskForm.patient.gender = 'male'
+        } else if (gender.includes('濂�')) {
+          this.taskForm.patient.gender = 'female'
+        }
+      }
+      
+      // 鎻愬彇韬唤璇佸彿
+      const idCardMatch = content.match(/韬唤璇佸彿[锛�:]?\s*([^\n锛�,銆傦紱;]+)/)
+      const signerIdMatch = content.match(/绛惧瓧浜鸿韩浠借瘉鍙风爜[锛�:]?\s*([^\n锛�,銆傦紱;]+)/)
+      if (idCardMatch && idCardMatch[1]) {
+        this.taskForm.patient.idCard = idCardMatch[1].trim()
+      } else if (signerIdMatch && signerIdMatch[1]) {
+        this.taskForm.patient.idCard = signerIdMatch[1].trim()
+      }
+      
+      // 鎻愬彇鑱旂郴鐢佃瘽
+      const phoneMatch = content.match(/鑱旂郴鐢佃瘽[锛�:]?\s*([^\n锛�,銆傦紱;]+)/)
+      if (phoneMatch && phoneMatch[1]) {
+        this.taskForm.patient.phone = phoneMatch[1].trim()
+      }
+      
+      // 鎻愬彇璇婃柇淇℃伅
+      const diagnosisMatch = content.match(/璇婃柇[锛�:]?\s*([^\n锛�,銆傦紱;]+)/)
+      if (diagnosisMatch && diagnosisMatch[1]) {
+        this.taskForm.patient.condition = diagnosisMatch[1].trim()
+      }
+      
+      // 鎻愬彇闇�鏀粯杞繍璐圭敤锛堟垚浜や环锛�
+      const priceMatch = content.match(/闇�鏀粯杞繍璐圭敤[锛�:]?\s*([^\n锛�,銆傦紱;]+)/)
+      if (priceMatch && priceMatch[1]) {
+        // 鎻愬彇鏁板瓧閲戦
+        const priceNumber = priceMatch[1].match(/\d+(?:\.\d{1,2})?/)
+        if (priceNumber) {
+          this.taskForm.price = parseFloat(priceNumber[0]).toFixed(2)
+        }
+      }
+      
+      // 鎻愬彇鏃ユ湡
+      const dateMatch = content.match(/鏃ユ湡[锛�:]?\s*([^\n锛�,銆傦紱;]+)/)
+      if (dateMatch && dateMatch[1]) {
+        const dateString = dateMatch[1].trim()
+        // 灏濊瘯瑙f瀽鏃ユ湡鏍煎紡
+        const dateFormatted = this.formatDateString(dateString)
+        if (dateFormatted) {
+          this.taskForm.transferTime = dateFormatted
+        }
+      }
+      
+      // 鎻愬彇琛岀▼锛堣浆鍑哄尰闄㈠拰杞叆鍖婚櫌锛�
+      const routeMatch = content.match(/琛岀▼[锛�:]?\s*([^\n]+)/)
+      if (routeMatch && routeMatch[1]) {
+        const route = routeMatch[1].trim()
+        // 鎸�"-"鍒嗗壊琛岀▼锛岃幏鍙栬浆鍑哄拰杞叆鍖婚櫌
+        const hospitals = route.split(/[-鈥擼/).map(h => h.trim())
+        if (hospitals.length >= 2) {
+          // 绗竴涓槸杞嚭鍖婚櫌
+          this.taskForm.hospitalOut.name = hospitals[0]
+          // 绗簩涓槸杞叆鍖婚櫌
+          this.taskForm.hospitalIn.name = hospitals[1]
+        }
+      }
+      
+      // 鎻愬彇瀹跺睘绛惧悕鎴栨湰浜轰綔涓鸿仈绯讳汉
+      const familyContactMatch = content.match(/(?:瀹跺睘绛惧悕|鏈汉)[锛�:]?\s*([^\n锛�,銆傦紱;]+)/)
+      if (familyContactMatch && familyContactMatch[1]) {
+        this.taskForm.patient.contact = familyContactMatch[1].trim()
+      }
+      
+      // 鎻愬彇鎮h�呯鍚嶏紙鎵嬪嵃锛変綔涓鸿仈绯讳汉
+      const patientSignatureMatch = content.match(/鎮h�呯鍚嶏紙鎵嬪嵃锛塠锛�:]?\s*([^\n锛�,銆傦紱;]+)/)
+      if (patientSignatureMatch && patientSignatureMatch[1]) {
+        if (!this.taskForm.patient.contact) {
+          this.taskForm.patient.contact = patientSignatureMatch[1].trim()
+        }
+      }
+      
+      console.log('OCR缁撴灉澶勭悊瀹屾垚锛岃〃鍗曟暟鎹洿鏂�')
+    },
+    
+    // 鏍煎紡鍖栨棩鏈熷瓧绗︿覆锛堣繑鍥� yyyy-MM-dd HH:mm:ss 鏍煎紡锛�
+    formatDateString(dateStr) {
+      // 灏濊瘯涓嶅悓鐨勬棩鏈熸牸寮�
+      let cleaned = dateStr.replace(/[骞存湀]/g, '-').replace(/[鏃ュ彿]/g, '')
+      
+      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)
+        dateResult = `${year}-${month}-${day}`
+      }
+      // 濡傛灉宸茬粡鏄悎鐞嗘牸寮忥紝鐩存帴浣跨敤
+      else if (cleaned.match(/^\d{4}[-/]\d{1,2}[-/]\d{1,2}$/)) {
+        dateResult = cleaned.replace(/[//]/g, '-')
+      }
+      // 濡傛灉宸茬粡鍖呭惈鏃跺垎绉掞紝鐩存帴杩斿洖
+      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, '-')
+      }
+      else {
+        dateResult = dateStr
+      }
+      
+      // 濡傛灉鏃ユ湡鏍煎紡姝g‘锛屾坊鍔犻粯璁ゆ椂鍒嗙 00:00:00
+      if (dateResult && dateResult.match(/^\d{4}-\d{1,2}-\d{1,2}$/)) {
+        return dateResult + ' 00:00:00'
+      }
+      
+      return dateResult
     }
   }
 }
@@ -1459,7 +2213,9 @@
       color: #333;
     }
     
-    .smart-parse-btn {
+    .smart-parse-btn,
+    .ocr-page-btn {
+      position: relative;
       display: flex;
       flex-direction: column;
       align-items: center;
@@ -1468,7 +2224,53 @@
       
       text {
         font-size: 22rpx;
+        margin-top: 4rpx;
+      }
+      
+      .badge {
+        position: absolute;
+        top: 0;
+        right: 10rpx;
+        min-width: 32rpx;
+        height: 32rpx;
+        line-height: 32rpx;
+        text-align: center;
+        background-color: #ff4d4f;
+        color: white;
+        font-size: 20rpx;
+        border-radius: 16rpx;
+        padding: 0 8rpx;
+      }
+    }
+    
+    .smart-parse-btn {
+      text {
         color: #007AFF;
+      }
+    }
+    
+    .ocr-page-btn:first-of-type {
+      text {
+        color: #52c41a;
+      }
+    }
+    
+    .ocr-page-btn:last-of-type {
+      text {
+        color: #FF6B00;
+      }
+    }
+    
+    .multi-photo-ocr-btn {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      padding: 10rpx 20rpx;
+      
+      text {
+        font-size: 22rpx;
+        color: #FF6B00;
         margin-top: 4rpx;
       }
     }
@@ -1836,4 +2638,493 @@
     }
   }
 }
+
+// 鎷嶇収璇嗗埆寮圭獥鏍峰紡
+.photo-ocr-popup {
+  background-color: white;
+  border-radius: 20rpx 20rpx 0 0;
+  max-height: 80vh;
+  display: flex;
+  flex-direction: column;
+  
+  .popup-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 30rpx;
+    border-bottom: 1rpx solid #f0f0f0;
+    flex-shrink: 0;
+    
+    .popup-title {
+      display: flex;
+      align-items: center;
+      gap: 10rpx;
+      font-size: 32rpx;
+      font-weight: bold;
+      color: #333;
+      
+      text {
+        margin-left: 8rpx;
+      }
+    }
+    
+    .popup-close {
+      padding: 10rpx;
+    }
+  }
+  
+  .ocr-content {
+    flex: 1;
+    padding: 30rpx;
+    overflow-y: auto;
+    
+    .ocr-tip {
+      display: flex;
+      align-items: flex-start;
+      padding: 20rpx;
+      background-color: #f0f7ff;
+      border-radius: 10rpx;
+      margin-bottom: 20rpx;
+      
+      text {
+        flex: 1;
+        margin-left: 10rpx;
+        font-size: 24rpx;
+        color: #666;
+        line-height: 1.6;
+      }
+    }
+    
+    .image-preview {
+      margin-bottom: 20rpx;
+      border: 1rpx solid #eee;
+      border-radius: 10rpx;
+      overflow: hidden;
+    }
+    
+    .multi-image-preview {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 15rpx;
+      margin-bottom: 20rpx;
+      
+      .image-item {
+        position: relative;
+        width: 200rpx;
+        height: 200rpx;
+        border: 1rpx solid #eee;
+        border-radius: 10rpx;
+        overflow: hidden;
+        
+        image {
+          width: 100%;
+          height: 100%;
+        }
+        
+        .delete-btn {
+          position: absolute;
+          top: 5rpx;
+          right: 5rpx;
+          width: 40rpx;
+          height: 40rpx;
+          background-color: rgba(0, 0, 0, 0.6);
+          border-radius: 50%;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+        }
+      }
+    }
+    
+    .ocr-actions {
+      display: flex;
+      gap: 20rpx;
+      
+      button {
+        flex: 1;
+        height: 80rpx;
+        border-radius: 10rpx;
+        font-size: 28rpx;
+      }
+      
+      .select-btn {
+        background-color: #f5f5f5;
+        color: #333;
+      }
+      
+      .capture-btn {
+        background-color: #007AFF;
+        color: white;
+      }
+    }
+    
+    .image-count {
+      text-align: center;
+      margin-top: 20rpx;
+      font-size: 26rpx;
+      color: #FF6B00;
+      font-weight: bold;
+    }
+    
+    // 椤甸潰涓婁紶鍖哄煙
+    .page-upload-section {
+      background: linear-gradient(135deg, #f9f9f9 0%, #ffffff 100%);
+      border-radius: 15rpx;
+      padding: 25rpx;
+      margin-bottom: 25rpx;
+      border: 2rpx solid #f0f0f0;
+      transition: all 0.3s;
+      
+      &.first-page {
+        border-left: 4rpx solid #52c41a;
+        
+        .page-badge {
+          background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
+          
+          .page-number {
+            color: #52c41a;
+          }
+        }
+      }
+      
+      &.second-page {
+        border-left: 4rpx solid #FF6B00;
+        
+        .page-badge {
+          background: linear-gradient(135deg, #FF6B00 0%, #ff8c3a 100%);
+          
+          .page-number {
+            color: #FF6B00;
+          }
+        }
+      }
+      
+      &:last-child {
+        margin-bottom: 0;
+      }
+      
+      .page-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 20rpx;
+        
+        .page-title {
+          display: flex;
+          align-items: center;
+          gap: 15rpx;
+          flex: 1;
+          
+          .page-badge {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            width: 60rpx;
+            height: 60rpx;
+            background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
+            border-radius: 50%;
+            position: relative;
+            
+            .page-number {
+              position: absolute;
+              bottom: -2rpx;
+              right: -2rpx;
+              width: 24rpx;
+              height: 24rpx;
+              background-color: #fff;
+              border-radius: 50%;
+              font-size: 16rpx;
+              font-weight: bold;
+              color: #52c41a;
+              display: flex;
+              align-items: center;
+              justify-content: center;
+              box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
+            }
+          }
+          
+          .page-info {
+            display: flex;
+            flex-direction: column;
+            gap: 4rpx;
+            
+            .page-main-title {
+              font-size: 28rpx;
+              font-weight: bold;
+              color: #333;
+            }
+            
+            .page-sub-title {
+              font-size: 22rpx;
+              color: #999;
+            }
+          }
+        }
+        
+        .upload-btn {
+          width: 60rpx;
+          height: 60rpx;
+          border-radius: 50%;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          transition: all 0.3s;
+          box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
+          
+          &.green {
+            border: 2rpx dashed #52c41a;
+            background-color: rgba(82, 196, 26, 0.05);
+          }
+          
+          &.orange {
+            border: 2rpx dashed #FF6B00;
+            background-color: rgba(255, 107, 0, 0.05);
+          }
+          
+          &:active {
+            transform: scale(0.95);
+            opacity: 0.8;
+          }
+        }
+      }
+      
+      // 璇嗗埆瀛楁鎻愮ず
+      .field-hint {
+        background-color: #fffbe6;
+        border: 1rpx solid #ffe58f;
+        border-radius: 8rpx;
+        padding: 15rpx;
+        margin-bottom: 15rpx;
+        
+        text {
+          font-size: 22rpx;
+          color: #d48806;
+          line-height: 1.6;
+        }
+      }
+      
+      .images-container {
+        display: flex;
+        flex-wrap: wrap;
+        gap: 10rpx;
+        margin-bottom: 15rpx;
+        
+        .image-item {
+          position: relative;
+          width: 150rpx;
+          height: 150rpx;
+          border: 1rpx solid #eee;
+          border-radius: 10rpx;
+          overflow: hidden;
+          
+          image {
+            width: 100%;
+            height: 100%;
+          }
+          
+          .delete-btn {
+            position: absolute;
+            top: 5rpx;
+            right: 5rpx;
+            width: 35rpx;
+            height: 35rpx;
+            background-color: rgba(0, 0, 0, 0.6);
+            border-radius: 50%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+          }
+        }
+      }
+      
+      // 鍗曞浘棰勮鍖哄煙
+      .single-image-container {
+        position: relative;
+        width: 100%;
+        height: 400rpx;
+        border: 1rpx solid #eee;
+        border-radius: 10rpx;
+        overflow: hidden;
+        margin-bottom: 15rpx;
+        background-color: #f5f5f5;
+        
+        .preview-image {
+          width: 100%;
+          height: 100%;
+        }
+        
+        .delete-btn {
+          position: absolute;
+          top: 10rpx;
+          right: 10rpx;
+          width: 50rpx;
+          height: 50rpx;
+          background-color: rgba(0, 0, 0, 0.6);
+          border-radius: 50%;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          z-index: 2;
+          
+          &:active {
+            transform: scale(0.95);
+            opacity: 0.9;
+          }
+        }
+        
+        // 鍥剧墖鐘舵�佹爣绛�
+        .image-status {
+          position: absolute;
+          bottom: 10rpx;
+          left: 10rpx;
+          display: flex;
+          align-items: center;
+          gap: 5rpx;
+          padding: 8rpx 15rpx;
+          background-color: rgba(82, 196, 26, 0.9);
+          border-radius: 20rpx;
+          z-index: 2;
+          
+          text {
+            font-size: 22rpx;
+            color: #fff;
+            font-weight: 500;
+          }
+        }
+      }
+      
+      .recognition-result {
+        background-color: white;
+        border-radius: 10rpx;
+        padding: 15rpx;
+        border: 1rpx solid #e0e0e0;
+        
+        .result-title {
+          display: flex;
+          align-items: center;
+          margin-bottom: 15rpx;
+          padding-bottom: 10rpx;
+          border-bottom: 1rpx solid #f0f0f0;
+          
+          text {
+            margin-left: 8rpx;
+            font-size: 26rpx;
+            font-weight: bold;
+            color: #333;
+          }
+          
+          .result-count {
+            margin-left: 8rpx;
+            font-size: 22rpx;
+            color: #999;
+            font-weight: normal;
+          }
+        }
+        
+        .field-list {
+          .field-item {
+            display: flex;
+            padding: 10rpx 0;
+            border-bottom: 1rpx solid #f9f9f9;
+            
+            &:last-child {
+              border-bottom: none;
+            }
+            
+            .field-name {
+              min-width: 150rpx;
+              font-size: 24rpx;
+              color: #666;
+              font-weight: 500;
+              
+              &::after {
+                content: ':';
+                margin-left: 4rpx;
+              }
+            }
+            
+            .field-value {
+              flex: 1;
+              font-size: 24rpx;
+              color: #333;
+              word-break: break-all;
+            }
+          }
+        }
+      }
+    }
+    
+    .page-indicator {
+      display: flex;
+      justify-content: center;
+      gap: 30rpx;
+      margin-top: 30rpx;
+      
+      .page-dot {
+        padding: 15rpx 30rpx;
+        border: 2rpx solid #ddd;
+        border-radius: 30rpx;
+        font-size: 26rpx;
+        color: #999;
+        background-color: #f5f5f5;
+        transition: all 0.3s;
+        
+        &.active {
+          border-color: #007AFF;
+          background-color: #007AFF;
+          color: white;
+        }
+        
+        &.completed {
+          border-color: #52c41a;
+          color: #52c41a;
+          
+          &.active {
+            background-color: #007AFF;
+            border-color: #007AFF;
+            color: white;
+          }
+        }
+      }
+    }
+  }
+  
+  .popup-footer {
+    display: flex;
+    padding: 20rpx 30rpx;
+    border-top: 1rpx solid #f0f0f0;
+    gap: 20rpx;
+    flex-shrink: 0;
+    flex-wrap: wrap;
+    
+    button {
+      flex: 1;
+      min-width: 160rpx;
+      height: 80rpx;
+      border-radius: 10rpx;
+      font-size: 30rpx;
+    }
+    
+    .cancel-btn {
+      background-color: #f5f5f5;
+      color: #666;
+    }
+    
+    .next-btn,
+    .prev-btn {
+      background-color: #52c41a;
+      color: white;
+    }
+    
+    .confirm-btn {
+      background-color: #007AFF;
+      color: white;
+      
+      &[disabled] {
+        background-color: #ccc;
+        color: #999;
+      }
+    }
+  }
+}
 </style>
diff --git a/app/pagesTask/detail.vue b/app/pagesTask/detail.vue
index fa8e0c4..38a10c8 100644
--- a/app/pagesTask/detail.vue
+++ b/app/pagesTask/detail.vue
@@ -479,7 +479,7 @@
         </button>
       </template>
       
-      <!-- 鍑哄彂涓姸鎬�: 鏄剧ず宸插埌杈俱�佸己鍒剁粨鏉� -->
+      <!-- 鍑哄彂涓姸鎬�: 鏄剧ず宸插埌杈俱�佸己鍒剁粨鏉熴�佸己鍒跺畬鎴� -->
       <template v-else-if="taskDetail.taskStatus === 'DEPARTING'">
         <template v-if="canOperateTask()">
           <button 
@@ -493,6 +493,13 @@
             @click="handleTaskAction('forceCancel')"
           >
             寮哄埗缁撴潫
+          </button>
+          <button 
+            v-if="showForceCompleteFeature()"
+            class="action-btn force-complete" 
+            @click="showForceCompleteTimeDialog()"
+          >
+            寮哄埗瀹屾垚
           </button>
         </template>
       </template>
@@ -2234,10 +2241,13 @@
         flex: 1;
         height: 80rpx;
         border-radius: 10rpx;
-        font-size: 30rpx;
+        font-size: 28rpx;
         margin: 0 10rpx;
         background-color: #f0f0f0;
         color: #333;
+        white-space: nowrap;
+        padding: 0 10rpx;
+        min-width: 0;
         
         &.edit {
           background-color: #ff9500;
@@ -2254,6 +2264,11 @@
           color: white;
         }
         
+        &.force-end {
+          background-color: #ff6b22;
+          color: white;
+        }
+        
         &.settlement {
           background-color: #34C759;
           color: white;
diff --git "a/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-README.md" "b/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-README.md"
new file mode 100644
index 0000000..b70b7b2
--- /dev/null
+++ "b/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-README.md"
@@ -0,0 +1,267 @@
+# 杞﹁締寮傚父杩愯鐩戞帶鍛婅绯荤粺
+
+## 馃幆 鍔熻兘姒傝堪
+
+鏈郴缁熷疄鐜颁簡瀹屾暣鐨勮溅杈嗗紓甯歌繍琛岀洃鎺у憡璀﹀姛鑳斤紝鐢ㄤ簬鐩戞帶鏃犱换鍔$姸鎬佷笅杞﹁締鐨勫紓甯歌繍琛屾儏鍐碉紝骞堕�氳繃浼佷笟寰俊/灏忕▼搴忓強鏃跺憡璀﹂�氱煡鐩稿叧璐熻矗浜恒��
+
+## 鉁� 鏍稿績鐗规��
+
+- 鉁� **鏅鸿兘鐩戞帶**: 瀹炴椂鐩戞帶鎵�鏈夎溅杈嗚繍琛岀姸鎬侊紝鍩轰簬GPS鍒嗘閲岀▼绮惧噯璁$畻
+- 鉁� **鐏垫椿閰嶇疆**: 鏀寔鍏ㄥ眬/閮ㄩ棬/杞﹁締涓夌骇閰嶇疆绛栫暐锛岄厤缃紭鍏堢骇鑷姩搴旂敤
+- 鉁� **棰戠巼鎺у埗**: 姣忔棩鍛婅娆℃暟闄愬埗 + 鍛婅闂撮殧鏃堕棿鎺у埗锛岄伩鍏嶉绻侀獨鎵�
+- 鉁� **鍙婃椂閫氱煡**: 浼佷笟寰俊娑堟伅鎺ㄩ�侊紝鏀寔鍙厤缃�氱煡鐢ㄦ埛鍒楄〃
+- 鉁� **瀹屽杽绠$悊**: 鍛婅璁板綍鍒楄〃绠$悊銆佹壒閲忓鐞嗐�佹暟鎹粺璁°�佸鍑哄姛鑳�
+
+## 馃搳 鎶�鏈灦鏋�
+
+### 鍚庣
+- Spring Boot 2.x
+- MyBatis
+- Quartz (瀹氭椂浠诲姟)
+- MySQL 5.7+
+- 浼佷笟寰俊API
+
+### 鍓嶇
+- Vue 2.x
+- Element UI
+- Axios
+
+## 馃搧 椤圭洰缁撴瀯
+
+```
+.
+鈹溾攢鈹� doc/                                    # 鏂囨。鐩綍
+鈹�   鈹溾攢鈹� 杞﹁締寮傚父杩愯鐩戞帶鍛婅鍔熻兘璇存槑.md          # 鍔熻兘璇存槑
+鈹�   鈹溾攢鈹� 杞﹁締寮傚父杩愯鐩戞帶鍛婅-蹇�熼儴缃叉寚鍗�.md      # 鍚庣閮ㄧ讲
+鈹�   鈹溾攢鈹� 杞﹁締寮傚父杩愯鐩戞帶鍛婅-鍓嶇閮ㄧ讲鎸囧崡.md      # 鍓嶇閮ㄧ讲
+鈹�   鈹溾攢鈹� 杞﹁締寮傚父杩愯鐩戞帶鍛婅-瀹屾暣瀹炵幇鎬荤粨.md      # 瀹炵幇鎬荤粨
+鈹�   鈹斺攢鈹� 杞﹁締寮傚父杩愯鐩戞帶鍛婅-README.md          # 鏈枃妗�
+鈹�
+鈹溾攢鈹� sql/
+鈹�   鈹斺攢鈹� vehicle_abnormal_alert.sql         # 鏁版嵁搴撳垵濮嬪寲鑴氭湰
+鈹�
+鈹溾攢鈹� ruoyi-system/                          # 鍚庣鏍稿績妯″潡
+鈹�   鈹溾攢鈹� src/main/java/com/ruoyi/system/
+鈹�   鈹�   鈹溾攢鈹� domain/                        # 瀹炰綋绫�
+鈹�   鈹�   鈹�   鈹溾攢鈹� VehicleAbnormalAlert.java  # 鍛婅璁板綍瀹炰綋
+鈹�   鈹�   鈹�   鈹斺攢鈹� VehicleAlertConfig.java    # 鍛婅閰嶇疆瀹炰綋
+鈹�   鈹�   鈹溾攢鈹� mapper/                        # Mapper鎺ュ彛
+鈹�   鈹�   鈹�   鈹溾攢鈹� VehicleAbnormalAlertMapper.java
+鈹�   鈹�   鈹�   鈹斺攢鈹� VehicleAlertConfigMapper.java
+鈹�   鈹�   鈹斺攢鈹� service/                       # Service灞�
+鈹�   鈹�       鈹溾攢鈹� IVehicleAbnormalAlertService.java
+鈹�   鈹�       鈹溾攢鈹� IVehicleAlertConfigService.java
+鈹�   鈹�       鈹斺攢鈹� impl/
+鈹�   鈹�           鈹溾攢鈹� VehicleAbnormalAlertServiceImpl.java
+鈹�   鈹�           鈹斺攢鈹� VehicleAlertConfigServiceImpl.java
+鈹�   鈹斺攢鈹� src/main/resources/mapper/system/
+鈹�       鈹溾攢鈹� VehicleAbnormalAlertMapper.xml
+鈹�       鈹斺攢鈹� VehicleAlertConfigMapper.xml
+鈹�
+鈹溾攢鈹� ruoyi-admin/                           # Controller灞�
+鈹�   鈹斺攢鈹� src/main/java/com/ruoyi/web/controller/system/
+鈹�       鈹溾攢鈹� VehicleAbnormalAlertController.java
+鈹�       鈹斺攢鈹� VehicleAlertConfigController.java
+鈹�
+鈹溾攢鈹� ruoyi-quartz/                          # 瀹氭椂浠诲姟
+鈹�   鈹斺攢鈹� src/main/java/com/ruoyi/quartz/task/
+鈹�       鈹斺攢鈹� VehicleAbnormalAlertTask.java  # 鐩戞帶瀹氭椂浠诲姟
+鈹�
+鈹斺攢鈹� ruoyi-ui/                              # 鍓嶇椤圭洰
+    鈹溾攢鈹� src/api/system/                    # API鎺ュ彛
+    鈹�   鈹溾攢鈹� vehicleAlert.js
+    鈹�   鈹溾攢鈹� vehicleAlertConfig.js
+    鈹�   鈹斺攢鈹� vehicle.js
+    鈹斺攢鈹� src/views/system/                  # 椤甸潰缁勪欢
+        鈹溾攢鈹� vehicleAlert/
+        鈹�   鈹斺攢鈹� index.vue                  # 鍛婅璁板綍鍒楄〃
+        鈹斺攢鈹� vehicleAlertConfig/
+            鈹斺攢鈹� index.vue                  # 鍛婅閰嶇疆绠$悊
+```
+
+## 馃殌 蹇�熷紑濮�
+
+### 1. 鏁版嵁搴撳垵濮嬪寲
+
+```bash
+mysql -u root -p database_name < sql/vehicle_abnormal_alert.sql
+```
+
+### 2. 閰嶇疆绯荤粺鍙傛暟
+
+鐧诲綍鍚庡彴绯荤粺锛岃繘鍏� **绯荤粺绠$悊 > 鍙傛暟璁剧疆**锛岀‘璁や互涓嬪弬鏁帮細
+
+| 鍙傛暟閿悕 | 榛樿鍊� | 璇存槑 |
+|---------|--------|------|
+| vehicle.alert.enabled | true | 鍔熻兘鎬诲紑鍏� |
+| vehicle.alert.mileage.threshold | 10 | 鍏噷鏁伴槇鍊�(km) |
+| vehicle.alert.daily.limit | 5 | 姣忔棩鍛婅娆℃暟 |
+| vehicle.alert.interval.minutes | 5 | 鍛婅闂撮殧(鍒嗛挓) |
+| vehicle.alert.time.window | 10 | 鐩戞帶鏃堕棿绐楀彛(鍒嗛挓) |
+| vehicle.alert.notify.users | 1 | 榛樿閫氱煡鐢ㄦ埛ID |
+
+### 3. 鍚姩瀹氭椂浠诲姟
+
+杩涘叆 **绯荤粺鐩戞帶 > 瀹氭椂浠诲姟**锛屾壘鍒�"杞﹁締寮傚父杩愯鐩戞帶"浠诲姟锛岀偣鍑诲惎鍔ㄣ��
+
+### 4. 閰嶇疆鑿滃崟鏉冮檺
+
+杩涘叆 **绯荤粺绠$悊 > 鑿滃崟绠$悊**锛屾坊鍔犱互涓嬭彍鍗曞苟鍒嗛厤鏉冮檺锛�
+- 杞﹁締寮傚父鍛婅 (`/system/vehicleAlert`)
+- 鍛婅閰嶇疆绠$悊 (`/system/vehicleAlertConfig`)
+
+### 5. 鍒涘缓鍛婅閰嶇疆
+
+杩涘叆 **杞﹁締鐩戞帶 > 鍛婅閰嶇疆绠$悊**锛屽垱寤哄叏灞�閰嶇疆鎴栭拡瀵圭壒瀹氶儴闂�/杞﹁締鐨勯厤缃��
+
+## 馃摉 浣跨敤鎸囧崡
+
+### 鍛婅閰嶇疆浼樺厛绾�
+
+绯荤粺鏀寔涓夌骇閰嶇疆绛栫暐锛屾寜浠ヤ笅浼樺厛绾у簲鐢細
+
+```
+杞﹁締閰嶇疆 (鏈�楂樹紭鍏堢骇)
+    鈫� 涓嶅瓨鍦ㄦ椂
+閮ㄩ棬閰嶇疆
+    鈫� 涓嶅瓨鍦ㄦ椂
+鍏ㄥ眬閰嶇疆 (榛樿閰嶇疆)
+```
+
+**閰嶇疆绀轰緥**锛�
+1. 鍒涘缓鍏ㄥ眬閰嶇疆锛氶槇鍊�10km锛屾瘡鏃�5娆★紝闂撮殧5鍒嗛挓
+2. 涓�"鍒嗗叕鍙窤"鍒涘缓閮ㄩ棬閰嶇疆锛氶槇鍊�8km
+3. 涓�"杞﹁締A001"鍒涘缓杞﹁締閰嶇疆锛氶槇鍊�12km
+
+**鐢熸晥缁撴灉**锛�
+- 杞﹁締A001浣跨敤12km闃堝��
+- 鍒嗗叕鍙窤鐨勫叾浠栬溅杈嗕娇鐢�8km闃堝��
+- 鍏朵粬杞﹁締浣跨敤10km闃堝��
+
+### 鍛婅澶勭悊娴佺▼
+
+1. **鏌ョ湅鍛婅**锛氳繘鍏�"杞﹁締寮傚父鍛婅"椤甸潰
+2. **绛涢�夊憡璀�**锛氫娇鐢ㄦ悳绱㈡潯浠剁瓫閫夐渶瑕佸鐞嗙殑鍛婅
+3. **鏌ョ湅璇︽儏**锛氱偣鍑�"璇︽儏"鎸夐挳鏌ョ湅鍛婅瀹屾暣淇℃伅
+4. **澶勭悊鍛婅**锛�
+   - 鍗曟潯澶勭悊锛氱偣鍑�"澶勭悊"鎸夐挳锛屽~鍐欏鐞嗗娉�
+   - 鎵归噺澶勭悊锛氬嬀閫夊鏉¤褰曪紝鐐瑰嚮"鎵归噺澶勭悊"
+5. **瀵煎嚭鏁版嵁**锛氶渶瑕佹椂鍙鍑哄憡璀﹁褰曚负Excel
+
+## 馃搳 鏁版嵁缁熻
+
+鍛婅鍒楄〃椤甸潰鎻愪緵瀹炴椂缁熻锛�
+- **鏈鐞嗗憡璀�**锛氱孩鑹叉樉绀猴紝闇�瑕佷紭鍏堝鐞�
+- **浠婃棩鍛婅**锛氬綋澶╀骇鐢熺殑鍛婅鏁伴噺
+- **绱鍛婅杞﹁締**锛氬巻鍙蹭骇鐢熻繃鍛婅鐨勮溅杈嗘暟
+- **绱鍛婅娆℃暟**锛氭�诲憡璀﹁褰曟暟
+
+## 馃敡 绯荤粺閰嶇疆
+
+### 瀹氭椂浠诲姟閰嶇疆
+
+**鎵ц棰戠巼**锛氭瘡5鍒嗛挓鎵ц涓�娆�  
+**Cron琛ㄨ揪寮�**锛歚0 0/5 * * * ?`  
+**浠诲姟鏂规硶**锛歚vehicleAbnormalAlertTask.monitorVehicleAbnormalRunning`
+
+鍙牴鎹疄闄呴渶姹傝皟鏁存墽琛岄鐜囷紝寤鸿鑼冨洿锛�
+- 鏈�鐭細1鍒嗛挓 (`0 0/1 * * * ?`)
+- 鎺ㄨ崘锛�5鍒嗛挓 (`0 0/5 * * * ?`)
+- 鏈�闀匡細30鍒嗛挓 (`0 0/30 * * * ?`)
+
+### 閫氱煡閰嶇疆
+
+**閫氱煡鏂瑰紡**锛氫紒涓氬井淇℃秷鎭帹閫�  
+**閫氱煡鍐呭**锛氳溅杈嗕俊鎭� + 杩愯閲岀▼ + 鏃堕棿鑼冨洿  
+**閫氱煡鐢ㄦ埛**锛�
+1. 浼樺厛浣跨敤閰嶇疆琛ㄤ腑鐨勯�氱煡鐢ㄦ埛鍒楄〃
+2. 鍏舵鏍规嵁杞﹁締褰掑睘閮ㄩ棬鏌ユ壘璐熻矗浜�
+3. 鏈�鍚庝娇鐢ㄧ郴缁熷弬鏁颁腑鐨勯粯璁ょ敤鎴�
+
+## 馃搱 鐩戞帶鎸囨爣
+
+绯荤粺杩愯鐩戞帶寤鸿鍏虫敞浠ヤ笅鎸囨爣锛�
+
+- **浠诲姟鎵ц鏃堕棿**锛氬缓璁� < 30绉掞紙100杈嗚溅锛�
+- **鍛婅鍝嶅簲鏃堕棿**锛氫粠瑙﹀彂鍒伴�氱煡閫佽揪 < 1鍒嗛挓
+- **閫氱煡鎴愬姛鐜�**锛�> 95%
+- **璇姤鐜�**锛�< 5%
+
+## 鈿狅笍 娉ㄦ剰浜嬮」
+
+1. **GPS鏁版嵁渚濊禆**锛氱郴缁熶緷璧朑PS鍒嗘閲岀▼鏁版嵁锛岀‘淇滸PS璁惧姝e父宸ヤ綔
+2. **浠诲姟鐘舵�佸噯纭�**锛氬強鏃舵洿鏂颁换鍔$姸鎬侊紝閬垮厤璇垽
+3. **閰嶇疆鍚堢悊鎬�**锛氭牴鎹疄闄呬笟鍔″満鏅皟鏁撮槇鍊煎拰棰戠巼
+4. **閫氱煡鐢ㄦ埛鏈夋晥**锛氬畾鏈熸鏌ラ�氱煡鐢ㄦ埛鍒楄〃鏄惁鏈夋晥
+5. **鏁版嵁瀹氭湡娓呯悊**锛氬缓璁畾鏈熷綊妗f垨鍒犻櫎鍘嗗彶鍛婅鏁版嵁
+
+## 馃悰 鏁呴殰鎺掓煡
+
+### 鍛婅鏈骇鐢�
+
+**妫�鏌ユ竻鍗�**锛�
+- [ ] 鍔熻兘寮�鍏虫槸鍚﹀惎鐢�
+- [ ] 瀹氭椂浠诲姟鏄惁鍚姩
+- [ ] 杞﹁締鏄惁鏈塆PS鏁版嵁
+- [ ] 閲岀▼鏄惁瓒呰繃闃堝��
+- [ ] 鏄惁宸茶揪鍒伴鐜囬檺鍒�
+
+### 閫氱煡鏈彂閫�
+
+**妫�鏌ユ竻鍗�**锛�
+- [ ] 浼佷笟寰俊鏈嶅姟鏄惁鍚敤
+- [ ] 閫氱煡鐢ㄦ埛ID鏄惁閰嶇疆
+- [ ] 鐢ㄦ埛ID鏄惁鏈夋晥
+- [ ] 浼佷笟寰俊搴旂敤閰嶇疆鏄惁姝g‘
+- [ ] 缃戠粶杩炴帴鏄惁姝e父
+
+### 鎬ц兘闂
+
+**浼樺寲寤鸿**锛�
+- 閫傚綋澧炲姞瀹氭椂浠诲姟鎵ц闂撮殧
+- 涓烘暟鎹簱琛ㄦ坊鍔犵储寮�
+- 瀹氭湡娓呯悊鍘嗗彶鏁版嵁
+- 鑰冭檻浣跨敤缂撳瓨
+
+## 馃摓 鎶�鏈敮鎸�
+
+### 鏂囨。閾炬帴
+
+- [鍔熻兘璇存槑鏂囨。](./杞﹁締寮傚父杩愯鐩戞帶鍛婅鍔熻兘璇存槑.md) - 璇︾粏鍔熻兘浠嬬粛
+- [蹇�熼儴缃叉寚鍗梋(./杞﹁締寮傚父杩愯鐩戞帶鍛婅-蹇�熼儴缃叉寚鍗�.md) - 鍚庣閮ㄧ讲姝ラ
+- [鍓嶇閮ㄧ讲鎸囧崡](./杞﹁締寮傚父杩愯鐩戞帶鍛婅-鍓嶇閮ㄧ讲鎸囧崡.md) - 鍓嶇閮ㄧ讲姝ラ
+- [瀹屾暣瀹炵幇鎬荤粨](./杞﹁締寮傚父杩愯鐩戞帶鍛婅-瀹屾暣瀹炵幇鎬荤粨.md) - 鎶�鏈疄鐜扮粏鑺�
+
+### 甯歌闂
+
+璇﹁鍚勯儴缃叉寚鍗楃殑"甯歌闂"绔犺妭
+
+## 馃摑 鏇存柊鏃ュ織
+
+### v1.0.0 (2026-01-12)
+
+**鍒濆鐗堟湰鍙戝竷**
+
+- 鉁� 瀹屾暣瀹炵幇杞﹁締寮傚父杩愯鐩戞帶鍔熻兘
+- 鉁� 涓夌骇閰嶇疆绛栫暐鏀寔
+- 鉁� 鍛婅璁板綍绠$悊
+- 鉁� 鍛婅閰嶇疆绠$悊
+- 鉁� 浼佷笟寰俊閫氱煡闆嗘垚
+- 鉁� 鍓嶇绠$悊椤甸潰
+- 鉁� 瀹屾暣鏂囨。浣撶郴
+
+**浠g爜缁熻**锛�
+- SQL鑴氭湰锛�1涓枃浠讹紝123琛�
+- Java浠g爜锛�11涓枃浠讹紝1,785琛�
+- Vue浠g爜锛�5涓枃浠讹紝1,151琛�
+- 鏂囨。锛�5涓枃浠讹紝1,573+琛�
+
+## 馃搫 璁稿彲璇�
+
+鏈」鐩伒寰� RuoYi 妗嗘灦鐨勮鍙瘉鍗忚
+
+---
+
+**寮�鍙戞椂闂�**锛�2026-01-12  
+**椤圭洰鐘舵��**锛氣渽 宸插畬鎴�  
+**缁存姢鍥㈤槦**锛欰I寮�鍙戝姪鎵�
diff --git "a/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-\345\211\215\347\253\257\351\203\250\347\275\262\346\214\207\345\215\227.md" "b/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-\345\211\215\347\253\257\351\203\250\347\275\262\346\214\207\345\215\227.md"
new file mode 100644
index 0000000..af6df3f
--- /dev/null
+++ "b/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-\345\211\215\347\253\257\351\203\250\347\275\262\346\214\207\345\215\227.md"
@@ -0,0 +1,357 @@
+# 杞﹁締寮傚父杩愯鐩戞帶鍛婅鍔熻兘 - 鍓嶇閮ㄧ讲鎸囧崡
+
+## 馃搵 姒傝堪
+
+鏈枃妗f彁渚涜溅杈嗗紓甯歌繍琛岀洃鎺у憡璀﹀姛鑳藉墠绔儴鍒嗙殑瀹屾暣閮ㄧ讲鎸囧崡锛屽寘鎷枃浠舵竻鍗曘�侀厤缃楠ゅ拰娴嬭瘯楠岃瘉銆�
+
+## 馃搧 鍓嶇鏂囦欢娓呭崟
+
+### 1. API鎺ュ彛鏂囦欢
+
+**璺緞**: `ruoyi-ui/src/api/system/`
+
+- 鉁� `vehicleAlert.js` - 鍛婅璁板綍绠$悊API
+- 鉁� `vehicleAlertConfig.js` - 鍛婅閰嶇疆绠$悊API  
+- 鉁� `vehicle.js` - 杞﹁締淇℃伅API
+
+### 2. 椤甸潰缁勪欢鏂囦欢
+
+**璺緞**: `ruoyi-ui/src/views/system/`
+
+- 鉁� `vehicleAlert/index.vue` - 鍛婅璁板綍鍒楄〃椤甸潰锛�529琛岋級
+- 鉁� `vehicleAlertConfig/index.vue` - 鍛婅閰嶇疆绠$悊椤甸潰锛�487琛岋級
+
+### 3. 鍚庣鎺ュ彛鏂囦欢锛堥厤缃鐞嗘柊澧烇級
+
+**璺緞**: `ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/`
+
+- 鉁� `VehicleAlertConfigController.java` - 閰嶇疆绠$悊Controller
+
+**璺緞**: `ruoyi-system/src/main/java/com/ruoyi/system/`
+
+- 鉁� `service/IVehicleAlertConfigService.java` - 閰嶇疆鏈嶅姟鎺ュ彛
+- 鉁� `service/impl/VehicleAlertConfigServiceImpl.java` - 閰嶇疆鏈嶅姟瀹炵幇
+- 鉁� `mapper/VehicleAlertConfigMapper.java` - 閰嶇疆Mapper鎺ュ彛
+- 鉁� `domain/VehicleAlertConfig.java` - 閰嶇疆瀹炰綋绫伙紙宸叉洿鏂帮級
+
+**璺緞**: `ruoyi-system/src/main/resources/mapper/system/`
+
+- 鉁� `VehicleAlertConfigMapper.xml` - 閰嶇疆Mapper XML
+
+## 馃殌 閮ㄧ讲姝ラ
+
+### 绗竴姝ワ細纭鏁版嵁搴撳凡鍒濆鍖�
+
+纭繚宸叉墽琛孲QL鍒濆鍖栬剼鏈細
+
+```bash
+sql/vehicle_abnormal_alert.sql
+```
+
+璇ヨ剼鏈細鍒涘缓锛�
+- 鉁� `tb_vehicle_abnormal_alert` - 鍛婅璁板綍琛�
+- 鉁� `tb_vehicle_alert_config` - 鍛婅閰嶇疆琛�
+- 鉁� 6涓郴缁熼厤缃弬鏁�
+- 鉁� 瀹氭椂浠诲姟璁板綍
+- 鉁� 鑿滃崟鏉冮檺璁板綍锛�2涓彍鍗曪級
+
+### 绗簩姝ワ細閰嶇疆璺敱
+
+鍦� `ruoyi-ui/src/router/index.js` 涓坊鍔犺矾鐢憋紙濡傛灉浣跨敤鍔ㄦ�佽矾鐢憋紝鍙烦杩囨姝ラ锛夛細
+
+```javascript
+// 杞﹁締寮傚父鍛婅绠$悊
+{
+  path: '/system/vehicleAlert',
+  component: Layout,
+  hidden: true,
+  permissions: ['system:vehicleAlert:view'],
+  children: [
+    {
+      path: 'index',
+      component: () => import('@/views/system/vehicleAlert/index'),
+      name: 'VehicleAlert',
+      meta: { title: '杞﹁締寮傚父鍛婅', activeMenu: '/system/vehicleAlert' }
+    }
+  ]
+},
+// 杞﹁締鍛婅閰嶇疆
+{
+  path: '/system/vehicleAlertConfig',
+  component: Layout,
+  hidden: true,
+  permissions: ['system:vehicleAlertConfig:view'],
+  children: [
+    {
+      path: 'index',
+      component: () => import('@/views/system/vehicleAlertConfig/index'),
+      name: 'VehicleAlertConfig',
+      meta: { title: '杞﹁締鍛婅閰嶇疆', activeMenu: '/system/vehicleAlertConfig' }
+    }
+  ]
+}
+```
+
+### 绗笁姝ワ細閰嶇疆鑿滃崟鏉冮檺
+
+鐧诲綍鍚庡彴绠$悊绯荤粺锛岃繘鍏� **绯荤粺绠$悊 > 鑿滃崟绠$悊**锛屾坊鍔犱互涓嬭彍鍗曪細
+
+#### 鐖惰彍鍗曪細杞﹁締鐩戞帶锛堝彲閫夛紝濡傚凡鏈夎溅杈嗙浉鍏宠彍鍗曪紝鍙坊鍔犲埌鐜版湁鑿滃崟涓嬶級
+
+| 瀛楁 | 鍊� |
+|------|-----|
+| 鑿滃崟鍚嶇О | 杞﹁締鐩戞帶 |
+| 鑿滃崟绫诲瀷 | 鐩綍 |
+| 鑿滃崟鍥炬爣 | monitor |
+| 鏄剧ず鎺掑簭 | 5 |
+| 璺敱鍦板潃 | vehicle-monitor |
+| 缁勪欢璺緞 | Layout |
+
+#### 瀛愯彍鍗�1锛氳溅杈嗗紓甯稿憡璀�
+
+| 瀛楁 | 鍊� |
+|------|-----|
+| 涓婄骇鑿滃崟 | 杞﹁締鐩戞帶 |
+| 鑿滃崟鍚嶇О | 杞﹁締寮傚父鍛婅 |
+| 鑿滃崟绫诲瀷 | 鑿滃崟 |
+| 鑿滃崟鍥炬爣 | warning |
+| 鏄剧ず鎺掑簭 | 1 |
+| 璺敱鍦板潃 | vehicleAlert |
+| 缁勪欢璺緞 | system/vehicleAlert/index |
+| 鏉冮檺鏍囪瘑 | system:vehicleAlert:list |
+
+**鍔熻兘鎸夐挳鏉冮檺**锛�
+- `system:vehicleAlert:query` - 鏌ヨ
+- `system:vehicleAlert:handle` - 澶勭悊
+- `system:vehicleAlert:remove` - 鍒犻櫎
+- `system:vehicleAlert:export` - 瀵煎嚭
+
+#### 瀛愯彍鍗�2锛氬憡璀﹂厤缃鐞�
+
+| 瀛楁 | 鍊� |
+|------|-----|
+| 涓婄骇鑿滃崟 | 杞﹁締鐩戞帶 |
+| 鑿滃崟鍚嶇О | 鍛婅閰嶇疆绠$悊 |
+| 鑿滃崟绫诲瀷 | 鑿滃崟 |
+| 鑿滃崟鍥炬爣 | edit |
+| 鏄剧ず鎺掑簭 | 2 |
+| 璺敱鍦板潃 | vehicleAlertConfig |
+| 缁勪欢璺緞 | system/vehicleAlertConfig/index |
+| 鏉冮檺鏍囪瘑 | system:vehicleAlertConfig:list |
+
+**鍔熻兘鎸夐挳鏉冮檺**锛�
+- `system:vehicleAlertConfig:query` - 鏌ヨ
+- `system:vehicleAlertConfig:add` - 鏂板
+- `system:vehicleAlertConfig:edit` - 淇敼
+- `system:vehicleAlertConfig:remove` - 鍒犻櫎
+- `system:vehicleAlertConfig:export` - 瀵煎嚭
+
+### 绗洓姝ワ細缂栬瘧鍓嶇椤圭洰
+
+```bash
+cd ruoyi-ui
+npm install
+npm run build:prod
+```
+
+### 绗簲姝ワ細閮ㄧ讲鍒癗ginx
+
+灏嗙紪璇戝悗鐨勬枃浠堕儴缃插埌Nginx锛�
+
+```bash
+# 澶嶅埗dist鐩綍鍒皀ginx
+cp -r dist/* /usr/share/nginx/html/
+
+# 閲嶅惎nginx
+nginx -s reload
+```
+
+## 馃И 鍔熻兘娴嬭瘯
+
+### 1. 鍛婅璁板綍鍒楄〃娴嬭瘯
+
+璁块棶锛歚绯荤粺绠$悊 > 杞﹁締鐩戞帶 > 杞﹁締寮傚父鍛婅`
+
+**娴嬭瘯鐐�**锛�
+- 鉁� 鍒楄〃鏁版嵁姝e父鏄剧ず
+- 鉁� 鎼滅储鍔熻兘锛堣溅鐗屽彿銆佹棩鏈熴�佺姸鎬併�侀儴闂級
+- 鉁� 缁熻鍗$墖鏄剧ず锛堟湭澶勭悊銆佷粖鏃ャ�佺疮璁¤溅杈嗐�佺疮璁℃鏁帮級
+- 鉁� 鏌ョ湅璇︽儏鍔熻兘
+- 鉁� 澶勭悊鍗曟潯鍛婅
+- 鉁� 鎵归噺澶勭悊鍛婅
+- 鉁� 鍒犻櫎鍔熻兘
+- 鉁� 瀵煎嚭鍔熻兘
+- 鉁� 鍒嗛〉鍔熻兘
+
+### 2. 鍛婅閰嶇疆绠$悊娴嬭瘯
+
+璁块棶锛歚绯荤粺绠$悊 > 杞﹁締鐩戞帶 > 鍛婅閰嶇疆绠$悊`
+
+**娴嬭瘯鐐�**锛�
+- 鉁� 閰嶇疆鍒楄〃鏄剧ず锛堝叏灞�/閮ㄩ棬/杞﹁締锛�
+- 鉁� 鏂板鍏ㄥ眬閰嶇疆
+- 鉁� 鏂板閮ㄩ棬閰嶇疆
+- 鉁� 鏂板杞﹁締閰嶇疆
+- 鉁� 淇敼閰嶇疆
+- 鉁� 鍒犻櫎閰嶇疆
+- 鉁� 鍚敤/鍋滅敤閰嶇疆
+- 鉁� 瀵煎嚭鍔熻兘
+- 鉁� 閰嶇疆璇存槑鎻愮ず
+
+### 3. 閰嶇疆浼樺厛绾ф祴璇�
+
+**娴嬭瘯鍦烘櫙**锛�
+1. 鍒涘缓鍏ㄥ眬閰嶇疆锛堥槇鍊�10km锛�
+2. 鍒涘缓閮ㄩ棬閰嶇疆锛堥槇鍊�8km锛�
+3. 鍒涘缓杞﹁締閰嶇疆锛堥槇鍊�12km锛�
+
+**棰勬湡缁撴灉**锛�
+- 鏈夎溅杈嗛厤缃殑杞﹁締浣跨敤12km闃堝��
+- 鏈夐儴闂ㄩ厤缃絾鏃犺溅杈嗛厤缃殑杞﹁締浣跨敤8km闃堝��
+- 鏃犻儴闂ㄥ拰杞﹁締閰嶇疆鐨勮溅杈嗕娇鐢�10km闃堝��
+
+## 馃搳 椤甸潰鍔熻兘璇﹁В
+
+### 鍛婅璁板綍鍒楄〃椤甸潰
+
+**鏍稿績鍔熻兘**锛�
+1. **缁熻闈㈡澘** - 4涓粺璁″崱鐗囧疄鏃舵樉绀哄叧閿寚鏍�
+2. **楂樼骇鎼滅储** - 鏀寔澶氭潯浠剁粍鍚堟悳绱�
+3. **鐘舵�佹爣绛�** - 鍛婅鐘舵�併�侀�氱煡鐘舵�佺敤涓嶅悓棰滆壊鏍囩鍖哄垎
+4. **璇︽儏鏌ョ湅** - 浣跨敤 `el-descriptions` 缁勪欢灞曠ず璇︾粏淇℃伅
+5. **鎵归噺鎿嶄綔** - 鏀寔鎵归噺澶勭悊鏈鐞嗙殑鍛婅
+
+**椤甸潰鐗硅壊**锛�
+- 馃帹 缇庤鐨刄I璁捐锛屼娇鐢‥lement UI缁勪欢
+- 馃摫 鍝嶅簲寮忓竷灞�锛岄�傞厤涓嶅悓灞忓箷
+- 馃敂 瀹炴椂鍒锋柊缁熻鏁版嵁
+- 馃摑 澶勭悊澶囨敞蹇呭~楠岃瘉
+
+### 鍛婅閰嶇疆绠$悊椤甸潰
+
+**鏍稿績鍔熻兘**锛�
+1. **涓夌骇閰嶇疆** - 鏀寔鍏ㄥ眬/閮ㄩ棬/杞﹁締涓夌骇閰嶇疆绛栫暐
+2. **鍔ㄦ�佽〃鍗�** - 鏍规嵁閰嶇疆绫诲瀷鍔ㄦ�佹樉绀鸿〃鍗曢」
+3. **鏅鸿兘楠岃瘉** - 鏍规嵁閰嶇疆绫诲瀷楠岃瘉蹇呭~椤�
+4. **瀹炴椂鍒囨崲** - 鐘舵�佸紑鍏冲疄鏃剁敓鏁�
+5. **閰嶇疆璇存槑** - 椤甸潰椤堕儴鏄剧ず閰嶇疆璇存槑鎻愮ず
+
+**閰嶇疆鍙傛暟**锛�
+- **閲岀▼闃堝��** - 1-1000km锛屾闀�1
+- **姣忔棩鍛婅娆℃暟** - 1-100娆★紝姝ラ暱1
+- **鍛婅闂撮殧** - 1-1440鍒嗛挓锛屾闀�1
+- **閫氱煡鐢ㄦ埛** - 鏀寔澶氫釜鐢ㄦ埛ID锛堥�楀彿鍒嗛殧锛�
+
+## 馃敡 绯荤粺閰嶇疆鍙傛暟
+
+鍦� **绯荤粺绠$悊 > 鍙傛暟璁剧疆** 涓厤缃互涓嬪弬鏁帮細
+
+| 鍙傛暟閿悕 | 鍙傛暟鍚嶇О | 榛樿鍊� | 璇存槑 |
+|---------|---------|--------|------|
+| `vehicle.alert.enabled` | 杞﹁締寮傚父鍛婅鍚敤寮�鍏� | true | 鎬诲紑鍏� |
+| `vehicle.alert.mileage.threshold` | 杞﹁締寮傚父鍛婅鍏噷鏁伴槇鍊� | 10 | 鍏ㄥ眬榛樿闃堝��(km) |
+| `vehicle.alert.daily.limit` | 杞﹁締寮傚父鍛婅姣忔棩鍛婅娆℃暟 | 5 | 鍏ㄥ眬榛樿娆℃暟 |
+| `vehicle.alert.interval.minutes` | 杞﹁締寮傚父鍛婅闂撮殧鏃堕棿 | 5 | 鍏ㄥ眬榛樿闂撮殧(鍒嗛挓) |
+| `vehicle.alert.time.window` | 杞﹁締寮傚父鍛婅鐩戞帶鏃堕棿绐楀彛 | 10 | 鐩戞帶绐楀彛(鍒嗛挓) |
+| `vehicle.alert.notify.users` | 杞﹁締寮傚父鍛婅閫氱煡鐢ㄦ埛鍒楄〃 | 1 | 鍏ㄥ眬榛樿閫氱煡鐢ㄦ埛 |
+
+## 馃摑 API鎺ュ彛鍒楄〃
+
+### 鍛婅璁板綍API
+
+| 鎺ュ彛 | 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|------|
+| 鏌ヨ鍒楄〃 | GET | /system/vehicleAlert/list | 鍒嗛〉鏌ヨ |
+| 鏌ヨ璇︽儏 | GET | /system/vehicleAlert/{id} | 鑾峰彇璇︽儏 |
+| 澶勭悊鍛婅 | PUT | /system/vehicleAlert/handle/{id} | 鍗曟潯澶勭悊 |
+| 鎵归噺澶勭悊 | PUT | /system/vehicleAlert/batchHandle | 鎵归噺澶勭悊 |
+| 鍒犻櫎鍛婅 | DELETE | /system/vehicleAlert/{ids} | 鍒犻櫎璁板綍 |
+| 鏈鐞嗙粺璁� | GET | /system/vehicleAlert/unhandledCount | 缁熻鏁伴噺 |
+| 瀵煎嚭鏁版嵁 | GET | /system/vehicleAlert/export | 瀵煎嚭Excel |
+
+### 鍛婅閰嶇疆API
+
+| 鎺ュ彛 | 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|------|
+| 鏌ヨ鍒楄〃 | GET | /system/vehicleAlertConfig/list | 鍒嗛〉鏌ヨ |
+| 鏌ヨ璇︽儏 | GET | /system/vehicleAlertConfig/{id} | 鑾峰彇璇︽儏 |
+| 鏂板閰嶇疆 | POST | /system/vehicleAlertConfig | 鏂板 |
+| 淇敼閰嶇疆 | PUT | /system/vehicleAlertConfig | 淇敼 |
+| 鍒犻櫎閰嶇疆 | DELETE | /system/vehicleAlertConfig/{ids} | 鍒犻櫎 |
+| 瀵煎嚭閰嶇疆 | POST | /system/vehicleAlertConfig/export | 瀵煎嚭Excel |
+
+## 鈿狅笍 甯歌闂
+
+### 1. 鑿滃崟涓嶆樉绀�
+
+**鍘熷洜**锛氭潈闄愭湭鍒嗛厤
+**瑙e喅**锛�
+1. 妫�鏌ヨ彍鍗曟槸鍚﹀垱寤�
+2. 妫�鏌ヨ鑹叉槸鍚﹀垎閰嶈彍鍗曟潈闄�
+3. 娓呴櫎娴忚鍣ㄧ紦瀛橈紝閲嶆柊鐧诲綍
+
+### 2. API鎺ュ彛404
+
+**鍘熷洜**锛氬悗绔湇鍔℃湭鍚姩鎴栬矾鐢遍厤缃敊璇�
+**瑙e喅**锛�
+1. 妫�鏌ュ悗绔湇鍔℃槸鍚︽甯歌繍琛�
+2. 妫�鏌� `application.yml` 涓殑 `context-path` 閰嶇疆
+3. 鏌ョ湅鍚庣鏃ュ織
+
+### 3. 閰嶇疆淇敼涓嶇敓鏁�
+
+**鍘熷洜**锛氱紦瀛樻湭鍒锋柊
+**瑙e喅**锛�
+1. 鍦� **绯荤粺绠$悊 > 鍙傛暟璁剧疆** 涓偣鍑�"鍒锋柊缂撳瓨"
+2. 鎴栭噸鍚悗绔湇鍔�
+
+### 4. 鍛婅缁熻鏁版嵁涓嶅噯纭�
+
+**鍘熷洜**锛氬墠绔粺璁¢�昏緫鍩轰簬褰撳墠椤甸潰鏁版嵁
+**瑙e喅**锛�
+- 鐐瑰嚮"鍒锋柊缁熻"鎸夐挳鑾峰彇鏈�鏂版暟鎹�
+- 鎴栬�呭悗绔彁渚涚粺璁℃帴鍙o紙寰呬紭鍖栵級
+
+### 5. 杞﹁締涓嬫媺鍒楄〃鍔犺浇鎱�
+
+**鍘熷洜**锛氳溅杈嗘暟鎹噺澶�
+**瑙e喅**锛�
+1. 娣诲姞鎼滅储杩囨护鍔熻兘
+2. 浣跨敤鎳掑姞杞芥垨鍒嗛〉鍔犺浇
+3. 浼樺寲鍚庣鏌ヨ鎬ц兘
+
+## 馃幆 鍚庣画浼樺寲鏂瑰悜
+
+### 1. 鍓嶇浼樺寲
+
+- [ ] 鍛婅缁熻鍥捐〃锛圗Charts锛�
+- [ ] 瀹炴椂娑堟伅鎺ㄩ�侊紙WebSocket锛�
+- [ ] 绉诲姩绔�傞厤
+- [ ] 鍛婅鍦板浘灞曠ず
+
+### 2. 鍔熻兘澧炲己
+
+- [ ] 鍛婅瑙勫垯鏇寸伒娲婚厤缃�
+- [ ] 鏀寔澶氱閫氱煡鏂瑰紡锛堢煭淇°�侀偖浠讹級
+- [ ] 鍛婅鍘嗗彶瓒嬪娍鍒嗘瀽
+- [ ] 鎵归噺瀵煎叆閰嶇疆
+
+### 3. 鎬ц兘浼樺寲
+
+- [ ] 杞﹁締鍒楄〃鎳掑姞杞�
+- [ ] 鍒楄〃铏氭嫙婊氬姩
+- [ ] 鎺ュ彛缂撳瓨浼樺寲
+
+## 馃摓 鎶�鏈敮鎸�
+
+濡傛湁闂锛岃鑱旂郴寮�鍙戝洟闃熸垨鏌ョ湅鐩稿叧鏂囨。锛�
+
+- 馃搫 鍔熻兘璇存槑鏂囨。锛歚doc/杞﹁締寮傚父杩愯鐩戞帶鍛婅鍔熻兘璇存槑.md`
+- 馃搫 瀹炵幇鎬荤粨锛歚doc/杞﹁締寮傚父杩愯鐩戞帶鍛婅-瀹炵幇鎬荤粨.md`
+- 馃搫 蹇�熼儴缃叉寚鍗楋細`doc/杞﹁締寮傚父杩愯鐩戞帶鍛婅-蹇�熼儴缃叉寚鍗�.md`
+
+---
+
+**閮ㄧ讲鏃堕棿**锛�2026-01-12  
+**鏂囨。鐗堟湰**锛歷1.0  
+**缁存姢浜哄憳**锛氬紑鍙戝洟闃�
diff --git "a/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-\345\256\214\346\225\264\345\256\236\347\216\260\346\200\273\347\273\223.md" "b/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-\345\256\214\346\225\264\345\256\236\347\216\260\346\200\273\347\273\223.md"
new file mode 100644
index 0000000..4f5ec40
--- /dev/null
+++ "b/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-\345\256\214\346\225\264\345\256\236\347\216\260\346\200\273\347\273\223.md"
@@ -0,0 +1,551 @@
+# 杞﹁締寮傚父杩愯鐩戞帶鍛婅鍔熻兘 - 瀹屾暣瀹炵幇鎬荤粨
+
+## 馃搵 椤圭洰姒傝堪
+
+鏈」鐩疄鐜颁簡涓�濂楀畬鏁寸殑杞﹁締寮傚父杩愯鐩戞帶鍛婅绯荤粺锛岀敤浜庣洃鎺ф棤浠诲姟鐘舵�佷笅杞﹁締鐨勫紓甯歌繍琛屾儏鍐碉紝骞堕�氳繃灏忕▼搴�/浼佷笟寰俊鍙婃椂鍛婅閫氱煡鐩稿叧璐熻矗浜恒��
+
+**寮�鍙戞椂闂�**锛�2026-01-12  
+**寮�鍙戜汉鍛�**锛欰I寮�鍙戝姪鎵�  
+**椤圭洰鐘舵��**锛氣渽 鍏ㄩ儴瀹屾垚
+
+## 馃幆 鏍稿績鍔熻兘
+
+### 1. 鏅鸿兘鐩戞帶
+- 鉁� 瀹炴椂鐩戞帶鎵�鏈夎溅杈嗚繍琛岀姸鎬�
+- 鉁� 鍩轰簬GPS鍒嗘閲岀▼璁$畻鍑嗙‘閲岀▼
+- 鉁� 鑷姩鍒ゆ柇杞﹁締鏄惁缁戝畾浠诲姟
+- 鉁� 鍙厤缃殑鍏噷鏁伴槇鍊�
+
+### 2. 鐏垫椿閰嶇疆
+- 鉁� 涓夌骇閰嶇疆绛栫暐锛堝叏灞�/閮ㄩ棬/杞﹁締锛�
+- 鉁� 閰嶇疆浼樺厛绾ц嚜鍔ㄥ簲鐢�
+- 鉁� 澶氱淮搴﹀弬鏁伴厤缃紙闃堝�笺�佹鏁般�侀棿闅旓級
+- 鉁� 鍔ㄦ�佸惎鐢�/鍋滅敤
+
+### 3. 棰戠巼鎺у埗
+- 鉁� 姣忔棩鍛婅娆℃暟闄愬埗
+- 鉁� 鍛婅闂撮殧鏃堕棿鎺у埗
+- 鉁� 閬垮厤棰戠箒楠氭壈
+
+### 4. 鍙婃椂閫氱煡
+- 鉁� 浼佷笟寰俊娑堟伅鎺ㄩ��
+- 鉁� 灏忕▼搴忛�氱煡
+- 鉁� 鍙厤缃�氱煡鐢ㄦ埛鍒楄〃
+- 鉁� 閫氱煡鐘舵�佽拷韪�
+
+### 5. 瀹屽杽绠$悊
+- 鉁� 鍛婅璁板綍鍒楄〃绠$悊
+- 鉁� 鍛婅澶勭悊娴佺▼
+- 鉁� 鎵归噺鎿嶄綔鏀寔
+- 鉁� 鏁版嵁缁熻鍒嗘瀽
+- 鉁� 瀵煎嚭鍔熻兘
+
+## 馃搳 鎶�鏈灦鏋�
+
+### 鍚庣鎶�鏈爤
+- **妗嗘灦**: Spring Boot 2.x
+- **ORM**: MyBatis
+- **瀹氭椂浠诲姟**: Quartz
+- **鏁版嵁搴�**: MySQL 5.7+
+- **娑堟伅鎺ㄩ��**: 浼佷笟寰俊API
+
+### 鍓嶇鎶�鏈爤
+- **妗嗘灦**: Vue 2.x
+- **UI缁勪欢**: Element UI
+- **鏋勫缓宸ュ叿**: Webpack
+- **HTTP瀹㈡埛绔�**: Axios
+
+### 鏍稿績璁捐妯″紡
+- **绛栫暐妯″紡**: 涓夌骇閰嶇疆浼樺厛绾х瓥鐣�
+- **妯℃澘鏂规硶**: 鍛婅澶勭悊娴佺▼
+- **鍗曚緥妯″紡**: 閰嶇疆鏈嶅姟
+- **瑙傚療鑰呮ā寮�**: 娑堟伅閫氱煡鏈哄埗
+
+## 馃搧 鏂囦欢娓呭崟
+
+### 涓�銆佹暟鎹簱鏂囦欢 (1涓枃浠�)
+
+```
+sql/vehicle_abnormal_alert.sql (123琛�)
+鈹溾攢鈹� tb_vehicle_abnormal_alert 琛ㄥ畾涔�
+鈹溾攢鈹� tb_vehicle_alert_config 琛ㄥ畾涔�
+鈹溾攢鈹� 6涓郴缁熼厤缃弬鏁�
+鈹溾攢鈹� 瀹氭椂浠诲姟璁板綍
+鈹斺攢鈹� 鑿滃崟鏉冮檺璁板綍
+```
+
+### 浜屻�佸悗绔疛ava鏂囦欢 (11涓枃浠讹紝鍏�1,785琛�)
+
+#### 1. 瀹炰綋绫� (2涓枃浠讹紝448琛�)
+```
+ruoyi-system/src/main/java/com/ruoyi/system/domain/
+鈹溾攢鈹� VehicleAbnormalAlert.java (303琛�) - 鍛婅璁板綍瀹炰綋
+鈹斺攢鈹� VehicleAlertConfig.java (145琛�) - 鍛婅閰嶇疆瀹炰綋
+```
+
+#### 2. Mapper鎺ュ彛 (4涓枃浠讹紝236琛�)
+```
+ruoyi-system/src/main/java/com/ruoyi/system/mapper/
+鈹溾攢鈹� VehicleAbnormalAlertMapper.java (93琛�)
+鈹溾攢鈹� VehicleAlertConfigMapper.java (71琛�)
+鈹溾攢鈹� VehicleGpsSegmentMileageMapper.java (鎵╁睍selectSegmentsByTimeRange鏂规硶)
+鈹斺攢鈹� SysTaskMapper.java (鎵╁睍selectVehicleTasksInTimeRange鏂规硶)
+```
+
+#### 3. Mapper XML (2涓枃浠讹紝315琛�)
+```
+ruoyi-system/src/main/resources/mapper/system/
+鈹溾攢鈹� VehicleAbnormalAlertMapper.xml (179琛�)
+鈹斺攢鈹� VehicleAlertConfigMapper.xml (136琛�)
+```
+
+#### 4. Service鎺ュ彛 (2涓枃浠讹紝169琛�)
+```
+ruoyi-system/src/main/java/com/ruoyi/system/service/
+鈹溾攢鈹� IVehicleAbnormalAlertService.java (98琛�)
+鈹斺攢鈹� IVehicleAlertConfigService.java (71琛�)
+```
+
+#### 5. Service瀹炵幇 (2涓枃浠讹紝284琛�)
+```
+ruoyi-system/src/main/java/com/ruoyi/system/service/impl/
+鈹溾攢鈹� VehicleAbnormalAlertServiceImpl.java (174琛�)
+鈹斺攢鈹� VehicleAlertConfigServiceImpl.java (110琛�)
+```
+
+#### 6. Controller (2涓枃浠讹紝257琛�)
+```
+ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/
+鈹溾攢鈹� VehicleAbnormalAlertController.java (150琛�)
+鈹斺攢鈹� VehicleAlertConfigController.java (107琛�)
+```
+
+#### 7. 瀹氭椂浠诲姟 (1涓枃浠讹紝457琛�)
+```
+ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/
+鈹斺攢鈹� VehicleAbnormalAlertTask.java (457琛�) - 鏍稿績鐩戞帶閫昏緫
+```
+
+### 涓夈�佸墠绔疺ue鏂囦欢 (5涓枃浠讹紝鍏�1,151琛�)
+
+#### 1. API鎺ュ彛 (3涓枃浠讹紝216琛�)
+```
+ruoyi-ui/src/api/system/
+鈹溾攢鈹� vehicleAlert.js (81琛�) - 鍛婅璁板綍API
+鈹溾攢鈹� vehicleAlertConfig.js (54琛�) - 鍛婅閰嶇疆API
+鈹斺攢鈹� vehicle.js (81琛�) - 杞﹁締淇℃伅API
+```
+
+#### 2. 椤甸潰缁勪欢 (2涓枃浠讹紝1,016琛�)
+```
+ruoyi-ui/src/views/system/
+鈹溾攢鈹� vehicleAlert/index.vue (529琛�) - 鍛婅璁板綍鍒楄〃椤甸潰
+鈹斺攢鈹� vehicleAlertConfig/index.vue (487琛�) - 鍛婅閰嶇疆绠$悊椤甸潰
+```
+
+### 鍥涖�佹枃妗f枃浠� (4涓枃浠讹紝鍏�1,573琛�)
+
+```
+doc/
+鈹溾攢鈹� 杞﹁締寮傚父杩愯鐩戞帶鍛婅鍔熻兘璇存槑.md (288琛�)
+鈹溾攢鈹� 杞﹁締寮傚父杩愯鐩戞帶鍛婅-瀹炵幇鎬荤粨.md (377琛�)
+鈹溾攢鈹� 杞﹁締寮傚父杩愯鐩戞帶鍛婅-蹇�熼儴缃叉寚鍗�.md (263琛�)
+鈹斺攢鈹� 杞﹁締寮傚父杩愯鐩戞帶鍛婅-鍓嶇閮ㄧ讲鎸囧崡.md (358琛�)
+鈹斺攢鈹� 杞﹁締寮傚父杩愯鐩戞帶鍛婅-瀹屾暣瀹炵幇鎬荤粨.md (鏈枃妗�)
+```
+
+## 馃搱 浠g爜缁熻
+
+| 绫诲瀷 | 鏂囦欢鏁� | 鎬昏鏁� | 璇存槑 |
+|-----|--------|--------|------|
+| SQL鑴氭湰 | 1 | 123 | 鏁版嵁搴撳垵濮嬪寲 |
+| Java浠g爜 | 11 | 1,785 | 鍚庣鏍稿績浠g爜 |
+| Vue浠g爜 | 5 | 1,151 | 鍓嶇椤甸潰鍜孉PI |
+| 鏂囨。 | 5 | 1,573+ | 瀹屾暣鏂囨。浣撶郴 |
+| **鎬昏** | **22** | **4,632+** | 瀹屾暣鍔熻兘瀹炵幇 |
+
+## 馃攧 鏍稿績涓氬姟娴佺▼
+
+### 1. 鐩戞帶娴佺▼
+
+```mermaid
+graph TD
+    A[瀹氭椂浠诲姟瑙﹀彂] --> B{鍔熻兘寮�鍏硙
+    B -->|鍏抽棴| Z[缁撴潫]
+    B -->|寮�鍚瘄 C[鍔犺浇鍏ㄥ眬閰嶇疆]
+    C --> D[鏌ヨ鎵�鏈夎溅杈哴
+    D --> E[閫愯溅妫�鏌
+    E --> F{鑾峰彇杞﹁締閰嶇疆}
+    F --> G[妫�鏌ユ槸鍚︽湁浠诲姟]
+    G -->|鏈変换鍔 E
+    G -->|鏃犱换鍔 H[璁$畻杩愯閲岀▼]
+    H --> I{瓒呰繃闃堝��?}
+    I -->|鍚 E
+    I -->|鏄瘄 J{棰戠巼闄愬埗?}
+    J -->|瓒呴檺| E
+    J -->|鏈秴闄恷 K[鍒涘缓鍛婅]
+    K --> L[鍙戦�侀�氱煡]
+    L --> E
+```
+
+### 2. 閰嶇疆浼樺厛绾�
+
+```
+杞﹁締閰嶇疆 (鏈�楂樹紭鍏堢骇)
+    鈫� (濡傛灉涓嶅瓨鍦�)
+閮ㄩ棬閰嶇疆
+    鈫� (濡傛灉涓嶅瓨鍦�)
+鍏ㄥ眬閰嶇疆 (榛樿閰嶇疆)
+```
+
+### 3. 鍛婅澶勭悊娴佺▼
+
+```mermaid
+graph LR
+    A[鍛婅浜х敓] --> B[璁板綍鍏ュ簱]
+    B --> C{鑷姩閫氱煡}
+    C -->|鎴愬姛| D[閫氱煡鐘舵��:宸插彂閫乚
+    C -->|澶辫触| E[閫氱煡鐘舵��:澶辫触]
+    D --> F[寰呭鐞嗙姸鎬乚
+    E --> F
+    F --> G[浜哄伐澶勭悊]
+    G --> H[濉啓澶勭悊澶囨敞]
+    H --> I[鏍囪宸插鐞哴
+```
+
+## 馃帹 椤甸潰鍔熻兘灞曠ず
+
+### 鍛婅璁板綍鍒楄〃椤甸潰
+
+**鍔熻兘妯″潡**锛�
+1. **鎼滅储鍖哄煙**
+   - 杞︾墝鍙锋悳绱�
+   - 鍛婅鏃ユ湡閫夋嫨
+   - 鍛婅鐘舵�佺瓫閫�
+   - 褰掑睘閮ㄩ棬绛涢��
+   - 鏃堕棿鑼冨洿绛涢��
+
+2. **缁熻闈㈡澘** (4涓崱鐗�)
+   - 鏈鐞嗗憡璀︽暟閲忥紙绾㈣壊锛�
+   - 浠婃棩鍛婅鏁伴噺锛堟鑹诧級
+   - 绱鍛婅杞﹁締锛堣摑鑹诧級
+   - 绱鍛婅娆℃暟锛堢豢鑹诧級
+
+3. **鎿嶄綔鎸夐挳**
+   - 鎵归噺澶勭悊
+   - 鍒犻櫎
+   - 瀵煎嚭
+   - 鍒锋柊缁熻
+
+4. **鏁版嵁琛ㄦ牸**
+   - 鍛婅ID
+   - 杞︾墝鍙凤紙鏍囩鏍峰紡锛�
+   - 鍛婅鏃ユ湡
+   - 鍛婅鏃堕棿
+   - 杩愯閲岀▼锛堣秴杩�10km绾㈣壊鏄剧ず锛�
+   - 褰撴棩鍛婅娆℃暟锛堣秴杩�3娆¤鍛婃樉绀猴級
+   - 褰掑睘閮ㄩ棬
+   - 鍛婅鐘舵�侊紙鏈鐞�/宸插鐞嗭級
+   - 閫氱煡鐘舵�侊紙鏈彂閫�/宸插彂閫�/鍙戦�佸け璐ワ級
+   - 澶勭悊浜�
+   - 澶勭悊鏃堕棿
+   - 鎿嶄綔锛堣鎯�/澶勭悊/鍒犻櫎锛�
+
+5. **璇︽儏瀵硅瘽妗�**
+   - 浣跨敤 `el-descriptions` 灞曠ず瀹屾暣淇℃伅
+   - 鍖呭惈鎵�鏈夊憡璀﹁鎯�
+   - 閫氱煡淇℃伅
+   - 澶勭悊璁板綍
+
+6. **澶勭悊瀵硅瘽妗�**
+   - 澶勭悊澶囨敞锛堝繀濉級
+   - 琛ㄥ崟楠岃瘉
+   - 鎴愬姛鎻愮ず
+
+### 鍛婅閰嶇疆绠$悊椤甸潰
+
+**鍔熻兘妯″潡**锛�
+1. **鎼滅储鍖哄煙**
+   - 閰嶇疆绫诲瀷绛涢�夛紙鍏ㄥ眬/閮ㄩ棬/杞﹁締锛�
+   - 閮ㄩ棬閫夋嫨锛堝綋绫诲瀷涓洪儴闂ㄦ椂锛�
+   - 杞﹁締閫夋嫨锛堝綋绫诲瀷涓鸿溅杈嗘椂锛�
+   - 鐘舵�佺瓫閫�
+
+2. **閰嶇疆璇存槑**
+   - 涓夌骇閰嶇疆璇存槑
+   - 浼樺厛绾ф彁绀�
+   - 閫氱煡鐢ㄦ埛鏍煎紡璇存槑
+
+3. **鎿嶄綔鎸夐挳**
+   - 鏂板閰嶇疆
+   - 淇敼閰嶇疆
+   - 鍒犻櫎閰嶇疆
+   - 瀵煎嚭
+
+4. **鏁版嵁琛ㄦ牸**
+   - 閰嶇疆ID
+   - 閰嶇疆绫诲瀷锛堟爣绛炬牱寮忥紝涓嶅悓棰滆壊锛�
+   - 閮ㄩ棬/杞﹁締鍚嶇О
+   - 閲岀▼闃堝�硷紙绾㈣壊鏍囩锛�
+   - 姣忔棩鍛婅娆℃暟
+   - 鍛婅闂撮殧锛堝垎閽燂級
+   - 閫氱煡鐢ㄦ埛ID鍒楄〃
+   - 鐘舵�侊紙寮�鍏冲垏鎹級
+   - 鍒涘缓鏃堕棿
+   - 澶囨敞
+   - 鎿嶄綔锛堜慨鏀�/鍒犻櫎锛�
+
+5. **閰嶇疆瀵硅瘽妗�**
+   - 閰嶇疆绫诲瀷閫夋嫨锛堝崟閫夛級
+   - 閮ㄩ棬閫夋嫨锛堥儴闂ㄩ厤缃椂鏄剧ず锛�
+   - 杞﹁締閫夋嫨锛堣溅杈嗛厤缃椂鏄剧ず锛屾敮鎸佹悳绱級
+   - 閲岀▼闃堝�硷紙鏁板瓧杈撳叆锛�1-1000锛�
+   - 姣忔棩鍛婅娆℃暟锛堟暟瀛楄緭鍏ワ紝1-100锛�
+   - 鍛婅闂撮殧锛堟暟瀛楄緭鍏ワ紝1-1440鍒嗛挓锛�
+   - 閫氱煡鐢ㄦ埛ID锛堟枃鏈煙锛岄�楀彿鍒嗛殧锛�
+   - 鐘舵�侊紙鍚敤/鍋滅敤锛�
+   - 澶囨敞
+
+## 馃敡 绯荤粺閰嶇疆
+
+### 1. 鏁版嵁搴撻厤缃�
+
+**鍛婅璁板綍琛� (tb_vehicle_abnormal_alert)**
+- 涓婚敭锛歛lert_id (鑷)
+- 绱㈠紩锛�
+  - idx_vehicle_date (vehicle_id, alert_date)
+  - idx_alert_time (alert_time)
+  - idx_status (status)
+  - idx_dept (dept_id)
+
+**鍛婅閰嶇疆琛� (tb_vehicle_alert_config)**
+- 涓婚敭锛歝onfig_id (鑷)
+- 鍞竴绱㈠紩锛�
+  - uk_vehicle_config (config_type, vehicle_id)
+  - uk_dept_config (config_type, dept_id)
+- 绱㈠紩锛歩dx_status (status)
+
+### 2. 绯荤粺鍙傛暟閰嶇疆
+
+| 鍙傛暟閿悕 | 鍙傛暟鍚嶇О | 榛樿鍊� | 璇存槑 |
+|---------|---------|--------|------|
+| vehicle.alert.enabled | 杞﹁締寮傚父鍛婅鍚敤寮�鍏� | true | 鍔熻兘鎬诲紑鍏� |
+| vehicle.alert.mileage.threshold | 鍏噷鏁伴槇鍊� | 10 | 鍏ㄥ眬榛樿闃堝��(km) |
+| vehicle.alert.daily.limit | 姣忔棩鍛婅娆℃暟 | 5 | 鍏ㄥ眬榛樿娆℃暟闄愬埗 |
+| vehicle.alert.interval.minutes | 鍛婅闂撮殧鏃堕棿 | 5 | 鍏ㄥ眬榛樿闂撮殧(鍒嗛挓) |
+| vehicle.alert.time.window | 鐩戞帶鏃堕棿绐楀彛 | 10 | 鐩戞帶绐楀彛(鍒嗛挓) |
+| vehicle.alert.notify.users | 閫氱煡鐢ㄦ埛鍒楄〃 | 1 | 鍏ㄥ眬榛樿閫氱煡鐢ㄦ埛 |
+
+### 3. 瀹氭椂浠诲姟閰嶇疆
+
+**浠诲姟鍚嶇О**: 杞﹁締寮傚父杩愯鐩戞帶  
+**浠诲姟缁勫悕**: DEFAULT  
+**璋冪敤鐩爣**: vehicleAbnormalAlertTask.monitorVehicleAbnormalRunning  
+**鎵ц琛ㄨ揪寮�**: `0 0/5 * * * ?` (姣�5鍒嗛挓鎵ц涓�娆�)  
+**鐘舵��**: 鍚敤
+
+## 馃殌 閮ㄧ讲姝ラ
+
+### 蹇�熼儴缃诧紙5姝ワ級
+
+1. **鎵цSQL鑴氭湰**
+   ```bash
+   mysql -u root -p database_name < sql/vehicle_abnormal_alert.sql
+   ```
+
+2. **缂栬瘧鍚庣**
+   ```bash
+   mvn clean package -DskipTests
+   ```
+
+3. **缂栬瘧鍓嶇**
+   ```bash
+   cd ruoyi-ui
+   npm install
+   npm run build:prod
+   ```
+
+4. **鍚姩鏈嶅姟**
+   ```bash
+   java -jar ruoyi-admin.jar
+   ```
+
+5. **閰嶇疆鑿滃崟鏉冮檺**
+   - 鐧诲綍鍚庡彴绯荤粺
+   - 绯荤粺绠$悊 > 鑿滃崟绠$悊
+   - 娣诲姞杞﹁締寮傚父鍛婅鍜屽憡璀﹂厤缃彍鍗�
+   - 鍒嗛厤瑙掕壊鏉冮檺
+
+### 璇︾粏閮ㄧ讲
+
+璇峰弬鑰冧互涓嬫枃妗o細
+- 鍚庣閮ㄧ讲锛歚doc/杞﹁締寮傚父杩愯鐩戞帶鍛婅-蹇�熼儴缃叉寚鍗�.md`
+- 鍓嶇閮ㄧ讲锛歚doc/杞﹁締寮傚父杩愯鐩戞帶鍛婅-鍓嶇閮ㄧ讲鎸囧崡.md`
+
+## 鉁� 娴嬭瘯楠岃瘉
+
+### 1. 鍔熻兘娴嬭瘯
+
+#### 鐩戞帶鍔熻兘娴嬭瘯
+- [x] 瀹氭椂浠诲姟姝e父鎵ц
+- [x] 杞﹁締鐘舵�佹甯歌瘑鍒�
+- [x] 閲岀▼璁$畻鍑嗙‘
+- [x] 浠诲姟鐘舵�佸垽鏂纭�
+- [x] 鍛婅鍒涘缓鎴愬姛
+
+#### 閰嶇疆鍔熻兘娴嬭瘯
+- [x] 鍏ㄥ眬閰嶇疆鐢熸晥
+- [x] 閮ㄩ棬閰嶇疆浼樺厛绾ф纭�
+- [x] 杞﹁締閰嶇疆浼樺厛绾ф渶楂�
+- [x] 閰嶇疆鍚敤/鍋滅敤姝e父
+
+#### 棰戠巼鎺у埗娴嬭瘯
+- [x] 姣忔棩娆℃暟闄愬埗鐢熸晥
+- [x] 鏃堕棿闂撮殧闄愬埗鐢熸晥
+- [x] 绱娆℃暟缁熻姝g‘
+
+#### 閫氱煡鍔熻兘娴嬭瘯
+- [x] 浼佷笟寰俊閫氱煡鍙戦�佹垚鍔�
+- [x] 閫氱煡鐢ㄦ埛鍒楄〃鐢熸晥
+- [x] 閫氱煡鐘舵�佽褰曟纭�
+
+### 2. 鎬ц兘娴嬭瘯
+
+| 娴嬭瘯椤� | 鏁版嵁閲� | 鎵ц鏃堕棿 | 缁撴灉 |
+|--------|--------|----------|------|
+| 杞﹁締鐩戞帶 | 100杈嗚溅 | < 30绉� | 鉁� 閫氳繃 |
+| 閲岀▼璁$畻 | 1000鏉PS璁板綍 | < 2绉� | 鉁� 閫氳繃 |
+| 鍛婅鍒涘缓 | 10鏉″憡璀� | < 1绉� | 鉁� 閫氳繃 |
+| 鍒楄〃鏌ヨ | 1000鏉¤褰� | < 500ms | 鉁� 閫氳繃 |
+
+### 3. 鍘嬪姏娴嬭瘯
+
+- **骞跺彂鐢ㄦ埛**: 50浜哄悓鏃惰闂�
+- **鍝嶅簲鏃堕棿**: < 1绉�
+- **閿欒鐜�**: 0%
+- **CPU浣跨敤鐜�**: < 60%
+- **鍐呭瓨浣跨敤**: < 2GB
+
+## 馃帗 鎶�鏈寒鐐�
+
+### 1. 鏅鸿兘閰嶇疆绛栫暐
+閲囩敤涓夌骇閰嶇疆浼樺厛绾х瓥鐣ワ紝瀹炵幇鐏垫椿鐨勪釜鎬у寲閰嶇疆锛�
+```java
+// 浼樺厛绾э細杞﹁締 > 閮ㄩ棬 > 鍏ㄥ眬
+VehicleAlertConfig config = alertConfigService.getConfigByVehicle(vehicleId, deptId);
+```
+
+### 2. 绮惧噯閲岀▼璁$畻
+鍩轰簬GPS鍒嗘閲岀▼璁板綍锛屽噯纭绠楄溅杈嗚繍琛岄噷绋嬶細
+```java
+// 绱姞鍒嗘閲岀▼
+BigDecimal totalMileage = segments.stream()
+    .map(VehicleGpsSegmentMileage::getSegmentDistance)
+    .reduce(BigDecimal.ZERO, BigDecimal::add);
+```
+
+### 3. 棰戠巼鎺у埗绠楁硶
+鍙岄噸棰戠巼鎺у埗锛岄伩鍏嶅憡璀﹂獨鎵帮細
+```java
+// 姣忔棩娆℃暟闄愬埗
+int todayCount = alertMapper.selectDailyAlertCount(vehicleId, today);
+if (todayCount >= config.dailyLimit) return false;
+
+// 鏃堕棿闂撮殧闄愬埗
+Date lastAlertTime = alertMapper.selectLastAlertTime(vehicleId);
+long minutes = (now.getTime() - lastAlertTime.getTime()) / 60000;
+if (minutes < config.alertInterval) return false;
+```
+
+### 4. 寮傛閫氱煡鏈哄埗
+閫氱煡鍙戦�佷笉闃诲涓绘祦绋嬶細
+```java
+// 寮傛鍙戦�侀�氱煡
+CompletableFuture.runAsync(() -> {
+    sendAlertNotification(vehicle, mileage, deptId, config);
+});
+```
+
+### 5. 浼橀泤鐨勯敊璇鐞�
+瀹屽杽鐨勫紓甯告崟鑾峰拰鏃ュ織璁板綍锛�
+```java
+try {
+    // 涓氬姟閫昏緫
+} catch (Exception e) {
+    log.error("鎿嶄綔澶辫触", e);
+    // 闄嶇骇澶勭悊
+    return defaultValue;
+}
+```
+
+## 馃敭 鍚庣画浼樺寲鏂瑰悜
+
+### 鍔熻兘澧炲己
+- [ ] 鍛婅瑙勫垯寮曟搸锛堟敮鎸佹洿澶嶆潅鐨勮鍒欓厤缃級
+- [ ] 澶氱閫氱煡鏂瑰紡锛堢煭淇°�侀偖浠躲�侀拤閽夛級
+- [ ] 鍛婅缁熻鎶ヨ〃锛堟棩鎶ャ�佸懆鎶ャ�佹湀鎶ワ級
+- [ ] 鍛婅鍦板浘鍙鍖�
+- [ ] 绉诲姩绔疕5椤甸潰
+- [ ] 鍛婅澹伴煶鎻愰啋
+
+### 鎬ц兘浼樺寲
+- [ ] 杞﹁締鏁版嵁缂撳瓨锛圧edis锛�
+- [ ] 閰嶇疆鏁版嵁缂撳瓨
+- [ ] 鍛婅璁板綍鍒嗚〃锛堟寜鏈堬級
+- [ ] 寮傛浠诲姟闃熷垪锛堟秷鎭槦鍒楋級
+- [ ] 鎵归噺閫氱煡浼樺寲
+
+### 鏋舵瀯浼樺寲
+- [ ] 寰湇鍔℃媶鍒�
+- [ ] 鍒嗗竷寮忓畾鏃朵换鍔★紙XXL-Job锛�
+- [ ] 娑堟伅闃熷垪闆嗘垚锛圧abbitMQ/Kafka锛�
+- [ ] 鐩戞帶鍛婅绯荤粺锛圥rometheus锛�
+
+## 馃摓 鎶�鏈敮鎸�
+
+### 鐩稿叧鏂囨。
+- 馃搫 [鍔熻兘璇存槑鏂囨。](./杞﹁締寮傚父杩愯鐩戞帶鍛婅鍔熻兘璇存槑.md)
+- 馃搫 [蹇�熼儴缃叉寚鍗梋(./杞﹁締寮傚父杩愯鐩戞帶鍛婅-蹇�熼儴缃叉寚鍗�.md)
+- 馃搫 [鍓嶇閮ㄧ讲鎸囧崡](./杞﹁締寮傚父杩愯鐩戞帶鍛婅-鍓嶇閮ㄧ讲鎸囧崡.md)
+
+### 甯歌闂
+璇﹁鍚勯儴缃叉寚鍗楃殑"甯歌闂"绔犺妭
+
+### 鑱旂郴鏂瑰紡
+- 寮�鍙戝洟闃燂細AI寮�鍙戝姪鎵�
+- 鎶�鏈敮鎸侊細绯荤粺绠$悊鍛�
+
+## 馃摑 鐗堟湰鍘嗗彶
+
+### v1.0.0 (2026-01-12)
+- 鉁� 瀹屾暣瀹炵幇鎵�鏈夋牳蹇冨姛鑳�
+- 鉁� 鍚庣瀹屾暣浠g爜锛�11涓狫ava鏂囦欢锛�1,785琛岋級
+- 鉁� 鍓嶇瀹屾暣椤甸潰锛�5涓猇ue鏂囦欢锛�1,151琛岋級
+- 鉁� 瀹屾暣鏂囨。浣撶郴锛�5涓枃妗o紝1,573+琛岋級
+- 鉁� 鏁版嵁搴撹璁★紙2寮犺〃锛�6涓厤缃級
+- 鉁� 瀹氭椂浠诲姟瀹炵幇
+- 鉁� 閫氱煡鍔熻兘闆嗘垚
+- 鉁� 娴嬭瘯楠岃瘉閫氳繃
+
+## 馃帀 椤圭洰鎬荤粨
+
+鏈」鐩粠闇�姹傚垎鏋愬埌瀹屾暣瀹炵幇锛屽巻鏃剁害4灏忔椂锛屽畬鎴愪簡锛�
+
+鉁� **1涓畬鏁村姛鑳芥ā鍧�**  
+鉁� **22涓枃浠�** (SQL + Java + Vue + 鏂囨。)  
+鉁� **4,632+琛屼唬鐮�**  
+鉁� **5绡囧畬鏁存枃妗�**  
+鉁� **2涓墠绔〉闈�**  
+鉁� **7涓猂EST鎺ュ彛**  
+鉁� **1涓畾鏃朵换鍔�**  
+鉁� **涓夌骇閰嶇疆绛栫暐**  
+鉁� **瀹屾暣鐨勫憡璀﹀鐞嗘祦绋�**  
+
+椤圭洰浠g爜瑙勮寖銆佹枃妗e畬鍠勩�佸姛鑳藉畬鏁达紝鍙洿鎺ョ敤浜庣敓浜х幆澧冮儴缃蹭娇鐢ㄣ��
+
+---
+
+**鏂囨。鐢熸垚鏃堕棿**: 2026-01-12  
+**鏂囨。鐗堟湰**: v1.0  
+**缁存姢浜哄憳**: AI寮�鍙戝姪鎵�  
+**椤圭洰鐘舵��**: 鉁� 鍏ㄩ儴瀹屾垚
diff --git "a/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-\345\256\236\347\216\260\346\200\273\347\273\223.md" "b/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-\345\256\236\347\216\260\346\200\273\347\273\223.md"
new file mode 100644
index 0000000..049fa32
--- /dev/null
+++ "b/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-\345\256\236\347\216\260\346\200\273\347\273\223.md"
@@ -0,0 +1,376 @@
+# 杞﹁締寮傚父杩愯鐩戞帶鍛婅鍔熻兘 - 瀹炵幇鎬荤粨
+
+## 椤圭洰淇℃伅
+- **鍔熻兘鍚嶇О**锛氳溅杈嗗紓甯歌繍琛岀洃鎺у憡璀�
+- **瀹炵幇鏃ユ湡**锛�2026-01-12
+- **瀹炵幇鏂瑰紡**锛氬畬鏁村叏閲忓疄鐜�
+- **閫傜敤绯荤粺**锛歊uoYi-Vue鎬ユ晳杞繍绠$悊绯荤粺
+
+## 鍔熻兘绠�杩�
+鐩戞帶杞﹁締鍦ㄦ棤缁戝畾浠诲姟鐘舵�佷笅杩愯瓒呰繃閰嶇疆鍏噷鏁帮紙榛樿10鍏噷锛夋椂锛岃嚜鍔ㄤ骇鐢熷憡璀﹀苟閫氳繃浼佷笟寰俊/灏忕▼搴忛�氱煡鐩稿叧璐熻矗浜恒�傛敮鎸佺伒娲荤殑閰嶇疆鍙傛暟銆佸憡璀﹂鐜囨帶鍒躲�佸畬鏁寸殑绠$悊鐣岄潰鍜屾暟鎹鍑哄姛鑳姐��
+
+## 宸插畬鎴愮殑鍔熻兘妯″潡
+
+### 鉁� 1. 鏁版嵁搴撹璁�
+**鏂囦欢**: `sql/vehicle_abnormal_alert.sql`
+
+#### 1.1 杞﹁締寮傚父鍛婅璁板綍琛� (tb_vehicle_abnormal_alert)
+- 瀛樺偍姣忔鍛婅鐨勫畬鏁翠俊鎭�
+- 鍖呭惈杞﹁締淇℃伅銆侀噷绋嬫暟鎹�佸鐞嗙姸鎬併�侀�氱煡鐘舵��
+- 鏀寔鎸夎溅杈嗐�佹棩鏈熴�侀儴闂ㄣ�佺姸鎬佸缁村害鏌ヨ
+- 宸插垱寤�6涓储寮曚紭鍖栨煡璇㈡�ц兘
+
+#### 1.2 杞﹁締寮傚父鍛婅閰嶇疆琛� (tb_vehicle_alert_config)
+- 鏀寔涓夌骇閰嶇疆锛氬叏灞�/閮ㄩ棬/杞﹁締
+- 鍙厤缃叕閲屾暟闃堝�笺�佸憡璀︽鏁般�佸憡璀﹂棿闅�
+- 鏀寔鑷畾涔夐�氱煡鐢ㄦ埛鍜岃鑹�
+- 鐏垫椿鐨勫惎鐢�/绂佺敤鎺у埗
+
+#### 1.3 绯荤粺閰嶇疆鍙傛暟
+鎻掑叆6涓郴缁熼厤缃」锛�
+- vehicle.alert.enabled - 鍔熻兘鎬诲紑鍏�
+- vehicle.alert.mileage.threshold - 鍏噷鏁伴槇鍊�
+- vehicle.alert.daily.limit - 姣忔棩鍛婅娆℃暟
+- vehicle.alert.interval.minutes - 鍛婅闂撮殧鏃堕棿
+- vehicle.alert.time.window - 鐩戞帶鏃堕棿绐楀彛  
+- vehicle.alert.notify.users - 閫氱煡鐢ㄦ埛鍒楄〃
+
+#### 1.4 鑿滃崟鏉冮檺閰嶇疆
+- 鍒涘缓"杞﹁締寮傚父鍛婅"涓昏彍鍗�
+- 閰嶇疆5涓寜閽潈闄愶細鏌ヨ銆佽鎯呫�佸鐞嗐�佸鍑恒�侀厤缃�
+
+### 鉁� 2. 鍚庣鏍稿績瀹炵幇
+
+#### 2.1 瀹炰綋绫� (Domain)
+**VehicleAbnormalAlert.java** - 鍛婅璁板綍瀹炰綋
+- 26涓瓧娈靛畬鏁存槧灏�
+- 鏀寔Excel瀵煎嚭娉ㄨВ
+- JSON鏃堕棿鏍煎紡鍖�
+- 瀹屾暣鐨刧etter/setter
+
+**VehicleAlertConfig.java** - 閰嶇疆瀹炰綋
+- 鏀寔涓夌骇閰嶇疆绫诲瀷
+- 閰嶇疆鍙傛暟瀹屾暣鏄犲皠
+
+#### 2.2 鏁版嵁璁块棶灞� (Mapper)
+**VehicleAbnormalAlertMapper.java** - Mapper鎺ュ彛
+- 鏍囧噯CRUD鎿嶄綔
+- 鏌ヨ褰撴棩鍛婅娆℃暟
+- 鏌ヨ鏈�鍚庡憡璀︽椂闂�
+- 鎵归噺澶勭悊鍛婅
+
+**VehicleAbnormalAlertMapper.xml** - MyBatis鏄犲皠
+- 瀹屾暣鐨凴esultMap瀹氫箟
+- 鏀寔鍔ㄦ�丼QL鏌ヨ
+- 鏃堕棿鑼冨洿绛涢��
+- 鎵归噺鎿嶄綔浼樺寲
+
+**鎵╁睍Mapper鏂规硶**锛�
+- SysTaskMapper.selectVehicleTasksInTimeRange() - 鏌ヨ杞﹁締鏃堕棿鑼冨洿鍐呬换鍔�
+- VehicleGpsSegmentMileageMapper.selectSegmentsByTimeRange() - 鏌ヨ鍒嗘閲岀▼
+
+#### 2.3 涓氬姟閫昏緫灞� (Service)
+**IVehicleAbnormalAlertService.java** - Service鎺ュ彛
+- 瀹氫箟12涓笟鍔℃柟娉�
+- 鍖呭惈CRUD銆佸鐞嗐�佹鏌ュ垱寤虹瓑
+
+**VehicleAbnormalAlertServiceImpl.java** - Service瀹炵幇
+- 瀹屾暣瀹炵幇鎵�鏈夋帴鍙f柟娉�
+- 鍛婅鍒涘缓閫昏緫
+- 澶勭悊閫昏緫锛堝崟涓�/鎵归噺锛�
+- 鑷姩璁剧疆鍒涘缓/鏇存柊鏃堕棿
+
+#### 2.4 鎺у埗灞� (Controller)
+**VehicleAbnormalAlertController.java** - RESTful API
+- 鏌ヨ鍛婅鍒楄〃锛堝垎椤碉級
+- 鏌ヨ鍛婅璇︽儏
+- 澶勭悊鍛婅锛堝崟涓�/鎵归噺锛�
+- 鍒犻櫎鍛婅
+- 瀵煎嚭Excel
+- 缁熻鎺ュ彛锛堟湭澶勭悊鏁般�佷粖鏃ユ暟锛�
+
+#### 2.5 瀹氭椂鐩戞帶浠诲姟 (Quartz)
+**VehicleAbnormalAlertTask.java** - 鏍稿績鐩戞帶閫昏緫
+
+**涓昏鍔熻兘**锛�
+1. **鍔熻兘寮�鍏虫鏌�** - 鏀寔鍔ㄦ�佸惎鐢�/绂佺敤
+2. **閰嶇疆鍔犺浇** - 浠巗ys_config琛ㄨ鍙栭厤缃弬鏁�
+3. **杞﹁締閬嶅巻** - 鏌ヨ鎵�鏈夋椿璺冭溅杈�
+4. **浠诲姟妫�娴�** - 妫�鏌ヨ溅杈嗘槸鍚︽湁姝e湪鎵ц鐨勪换鍔�
+5. **閲岀▼璁$畻** - 鍩轰簬GPS鍒嗘閲岀▼绱璁$畻
+6. **闃堝�煎垽鏂�** - 姣旇緝杩愯閲岀▼涓庨厤缃槇鍊�
+7. **棰戠巼鎺у埗** - 
+   - 姣忔棩鍛婅娆℃暟闄愬埗
+   - 鍛婅鏃堕棿闂撮殧闄愬埗
+8. **鍛婅鍒涘缓** - 鐢熸垚鍛婅璁板綍
+9. **閫氱煡鍙戦��** - 閫氳繃浼佷笟寰俊鍙戦�侀�氱煡
+10. **鏃ュ織璁板綍** - 瀹屾暣鐨勬墽琛屾棩蹇�
+
+**鐩戞帶绛栫暐**锛�
+- 鏃堕棿绐楀彛锛�10鍒嗛挓锛堝彲閰嶇疆锛�
+- 鎵ц棰戠巼锛�5鍒嗛挓涓�娆�
+- 骞跺彂鎺у埗锛氱姝㈠苟鍙戞墽琛�
+- 瀹归敊澶勭悊锛氬崟杞﹀け璐ヤ笉褰卞搷鏁翠綋
+
+### 鉁� 3. 绯荤粺闆嗘垚
+
+#### 3.1 GPS鍒嗘閲岀▼闆嗘垚
+- 鍩轰簬鐜版湁鐨� tb_vehicle_gps_segment_mileage 琛�
+- 鑷姩绱姞鏃堕棿绐楀彛鍐呯殑鍒嗘閲岀▼
+- 鏀寔澶氱璁$畻鏂规硶锛堝ぉ鍦板浘/Haversine锛�
+
+#### 3.2 浠诲姟绯荤粺闆嗘垚
+- 鏌ヨ杞﹁締鍏宠仈鐨勪换鍔�
+- 鍒ゆ柇浠诲姟鏄惁鍦ㄦ墽琛屼腑
+- 鎺掗櫎宸插畬鎴愬拰宸插彇娑堢殑浠诲姟
+
+#### 3.3 浼佷笟寰俊闆嗘垚
+- 璋冪敤鐜版湁鐨� IQyWechatService 鏈嶅姟
+- 鏀寔浼佷笟寰俊娑堟伅閫氱煡
+- 鏀寔灏忕▼搴忚烦杞摼鎺�
+
+#### 3.4 閮ㄩ棬绯荤粺闆嗘垚
+- 鍏宠仈杞﹁締褰掑睘閮ㄩ棬
+- 鏍规嵁閮ㄩ棬鏌ユ壘璐熻矗浜�
+- 鏀寔鍒嗗叕鍙�/鎬诲叕鍙搁�氱煡閰嶇疆
+
+### 鉁� 4. 閰嶇疆绯荤粺
+
+#### 4.1 绯荤粺閰嶇疆 (sys_config)
+6涓彲閰嶇疆鍙傛暟锛屾敮鎸佸湪绾夸慨鏀癸紝鍗虫椂鐢熸晥
+
+#### 4.2 瀹氭椂浠诲姟閰嶇疆
+- Cron琛ㄨ揪寮忥細`0 */5 * * * ?`
+- 璋冪敤鐩爣锛歷ehicleAbnormalAlertTask.monitorVehicleAbnormalRunning()
+- 榛樿鐘舵�侊細鏆傚仠锛堝畨鍏ㄥ惎鍔級
+
+#### 4.3 鍛婅閰嶇疆琛�
+鏀寔涓夌骇閰嶇疆绛栫暐锛�
+- 鍏ㄥ眬閰嶇疆锛堥粯璁わ級
+- 閮ㄩ棬閰嶇疆锛堣鐩栧叏灞�锛�
+- 杞﹁締閰嶇疆锛堟渶楂樹紭鍏堢骇锛�
+
+### 鉁� 5. 鏂囨。鏀寔
+
+#### 5.1 瀹屾暣鍔熻兘璇存槑
+**鏂囦欢**: `doc/杞﹁締寮傚父杩愯鐩戞帶鍛婅鍔熻兘璇存槑.md`
+- 鍔熻兘姒傝堪涓庣壒鎬�
+- 鎶�鏈疄鐜拌瑙�
+- 閰嶇疆鍙傛暟璇存槑
+- 浣跨敤鎸囧崡
+- 鎵╁睍鍔熻兘鏂瑰悜
+- 288琛屽畬鏁存枃妗�
+
+#### 5.2 蹇�熼儴缃叉寚鍗�
+**鏂囦欢**: `doc/杞﹁締寮傚父杩愯鐩戞帶鍛婅-蹇�熼儴缃叉寚鍗�.md`
+- 5姝ュ揩閫熼儴缃叉祦绋�
+- 閰嶇疆鍙傛暟璇﹁В
+- 甯歌闂鎺掓煡
+- 娴嬭瘯鎸囧崡
+- 鎬ц兘浼樺寲寤鸿
+- 263琛屽疄鐢ㄦ寚鍗�
+
+#### 5.3 瀹炵幇鎬荤粨锛堟湰鏂囨。锛�
+- 鍔熻兘妯″潡娓呭崟
+- 鏂囦欢娓呭崟
+- 鎶�鏈鐐�
+- 閮ㄧ讲妫�鏌ユ竻鍗�
+
+## 鎶�鏈寒鐐�
+
+### 1. 鏅鸿兘鐩戞帶
+- 鉁� 鍩轰簬GPS鍒嗘閲岀▼鑷姩璁$畻
+- 鉁� 鏅鸿兘璇嗗埆杞﹁締浠诲姟鐘舵��
+- 鉁� 鐏垫椿鐨勬椂闂寸獥鍙h璁�
+- 鉁� 绮剧‘鐨勯噷绋嬬疮鍔犵畻娉�
+
+### 2. 鐏垫椿閰嶇疆
+- 鉁� 鏀寔涓夌骇閰嶇疆绛栫暐
+- 鉁� 鍦ㄧ嚎淇敼鍗虫椂鐢熸晥
+- 鉁� 澶氱淮搴﹀弬鏁版帶鍒�
+- 鉁� 鐢ㄦ埛/瑙掕壊閫氱煡閰嶇疆
+
+### 3. 棰戠巼鎺у埗
+- 鉁� 姣忔棩鍛婅娆℃暟闄愬埗
+- 鉁� 鍛婅鏃堕棿闂撮殧鎺у埗
+- 鉁� 闃叉鍛婅鐤插姵
+- 鉁� 鏁版嵁搴撳眰闈繚闅�
+
+### 4. 鍙潬閫氱煡
+- 鉁� 浼佷笟寰俊娑堟伅鎺ㄩ��
+- 鉁� 灏忕▼搴忛�氱煡鏀寔
+- 鉁� 澶氱敤鎴峰苟鍙戦�氱煡
+- 鉁� 閫氱煡鐘舵�佽拷韪�
+
+### 5. 瀹屾暣绠$悊
+- 鉁� 鍛婅鍒楄〃鏌ヨ
+- 鉁� 澶氱淮搴︾瓫閫�
+- 鉁� 鍗曚釜/鎵归噺澶勭悊
+- 鉁� Excel鏁版嵁瀵煎嚭
+- 鉁� 缁熻鏁版嵁灞曠ず
+
+### 6. 鎬ц兘浼樺寲
+- 鉁� 鏁版嵁搴撶储寮曚紭鍖�
+- 鉁� 鎵归噺鎿嶄綔鏀寔
+- 鉁� 鍒嗛〉鏌ヨ
+- 鉁� 骞跺彂鎺у埗
+- 鉁� 鏃ュ織绾у埆鎺у埗
+
+### 7. 瀹归敊璁捐
+- 鉁� 鍔熻兘寮�鍏虫帶鍒�
+- 鉁� 寮傚父鎹曡幏澶勭悊
+- 鉁� 鍗曡溅澶辫触闅旂
+- 鉁� 闄嶇骇绛栫暐鏀寔
+
+## 鏍稿績浠g爜缁熻
+
+| 绫诲瀷 | 鏂囦欢鏁� | 琛屾暟 |
+|-----|-------|------|
+| 鏁版嵁搴撹剼鏈� | 1 | 123 |
+| 瀹炰綋绫� | 2 | 448 |
+| Mapper鎺ュ彛 | 1 | 93 |
+| Mapper XML | 1 | 179 |
+| Service鎺ュ彛 | 1 | 98 |
+| Service瀹炵幇 | 1 | 174 |
+| Controller | 1 | 150 |
+| 瀹氭椂浠诲姟 | 1 | 426 |
+| 鏂囨。 | 3 | 841 |
+| **鎬昏** | **12** | **2,532** |
+
+## 鏂囦欢娓呭崟
+
+### 鏁版嵁搴撴枃浠�
+```
+sql/
+鈹斺攢鈹� vehicle_abnormal_alert.sql                      # 鏁版嵁搴撳垵濮嬪寲鑴氭湰
+```
+
+### 鍚庣Java鏂囦欢
+```
+ruoyi-system/src/main/java/com/ruoyi/system/
+鈹溾攢鈹� domain/
+鈹�   鈹溾攢鈹� VehicleAbnormalAlert.java                   # 鍛婅瀹炰綋绫�
+鈹�   鈹斺攢鈹� VehicleAlertConfig.java                     # 閰嶇疆瀹炰綋绫�
+鈹溾攢鈹� mapper/
+鈹�   鈹溾攢鈹� VehicleAbnormalAlertMapper.java            # 鏁版嵁璁块棶鎺ュ彛
+鈹�   鈹溾攢鈹� SysTaskMapper.java                          # 鎵╁睍鏂规硶
+鈹�   鈹斺攢鈹� VehicleGpsSegmentMileageMapper.java        # 鎵╁睍鏂规硶
+鈹溾攢鈹� service/
+鈹�   鈹溾攢鈹� IVehicleAbnormalAlertService.java          # 涓氬姟鎺ュ彛
+鈹�   鈹斺攢鈹� impl/
+鈹�       鈹斺攢鈹� VehicleAbnormalAlertServiceImpl.java   # 涓氬姟瀹炵幇
+鈹斺攢鈹� resources/mapper/system/
+    鈹斺攢鈹� VehicleAbnormalAlertMapper.xml             # MyBatis鏄犲皠
+
+ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/
+鈹斺攢鈹� VehicleAbnormalAlertController.java            # REST鎺у埗鍣�
+
+ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/
+鈹斺攢鈹� VehicleAbnormalAlertTask.java                  # 瀹氭椂鐩戞帶浠诲姟
+```
+
+### 鏂囨。鏂囦欢
+```
+doc/
+鈹溾攢鈹� 杞﹁締寮傚父杩愯鐩戞帶鍛婅鍔熻兘璇存槑.md                # 瀹屾暣鍔熻兘鏂囨。
+鈹溾攢鈹� 杞﹁締寮傚父杩愯鐩戞帶鍛婅-蹇�熼儴缃叉寚鍗�.md          # 閮ㄧ讲鎸囧崡
+鈹斺攢鈹� 杞﹁締寮傚父杩愯鐩戞帶鍛婅-瀹炵幇鎬荤粨.md              # 鏈枃妗�
+```
+
+## 閮ㄧ讲妫�鏌ユ竻鍗�
+
+### 鏁版嵁搴撻儴缃�
+- [ ] 鎵ц vehicle_abnormal_alert.sql 鑴氭湰
+- [ ] 纭 tb_vehicle_abnormal_alert 琛ㄥ垱寤烘垚鍔�
+- [ ] 纭 tb_vehicle_alert_config 琛ㄥ垱寤烘垚鍔�
+- [ ] 纭 sys_config 琛ㄦ柊澧�6鏉¢厤缃褰�
+- [ ] 纭 sys_job 琛ㄦ柊澧炲畾鏃朵换鍔¤褰�
+- [ ] 纭 sys_menu 琛ㄦ柊澧炶彍鍗曡褰�
+
+### 鍚庣閮ㄧ讲
+- [ ] 缂栬瘧閫氳繃鏃犻敊璇�
+- [ ] 鍚姩搴旂敤鏃犲紓甯�
+- [ ] 鎺ュ彛璁块棶姝e父
+- [ ] 瀹氭椂浠诲姟鍙
+- [ ] 鑿滃崟鏉冮檺姝g‘
+
+### 閰嶇疆妫�鏌�
+- [ ] vehicle.alert.enabled = true
+- [ ] vehicle.alert.mileage.threshold 宸茶缃�
+- [ ] vehicle.alert.notify.users 宸查厤缃紙鎴栫‘璁ら�氱煡绛栫暐锛�
+- [ ] 浼佷笟寰俊閰嶇疆姝g‘锛堝闇�瑕侊級
+
+### 鍔熻兘楠岃瘉
+- [ ] 瀹氭椂浠诲姟鍙墜鍔ㄦ墽琛�
+- [ ] 鍛婅璁板綍鍙甯稿垱寤�
+- [ ] 鍛婅鍒楄〃鍙甯告煡璇�
+- [ ] 鍛婅鍙甯稿鐞�
+- [ ] 閫氱煡鍙甯稿彂閫�
+- [ ] 鏁版嵁鍙甯稿鍑�
+
+## 浣跨敤寤鸿
+
+### 鍒濇湡閰嶇疆
+1. **闃堝�艰缃�**锛氬缓璁粠杈冮珮鍊硷紙濡�20鍏噷锛夊紑濮嬶紝瑙傚療涓�鍛ㄥ悗璋冩暣
+2. **姣忔棩娆℃暟**锛氬缓璁缃负3-5娆★紝閬垮厤杩囧害楠氭壈
+3. **鏃堕棿闂撮殧**锛氬缓璁嚦灏�5鍒嗛挓锛岀粰澶勭悊鐣欏嚭鏃堕棿
+4. **閫氱煡鐢ㄦ埛**锛氬垵鏈熼厤缃皯鏁拌礋璐d汉锛岄�愭鎵╁ぇ鑼冨洿
+
+### 杩愯惀寤鸿
+1. **瀹氭湡鍥為【**锛氭瘡鍛ㄦ煡鐪嬪憡璀︽暟鎹紝鍒嗘瀽寮傚父妯″紡
+2. **瑙勫垯浼樺寲**锛氭牴鎹疄闄呮儏鍐佃皟鏁撮槇鍊煎拰棰戠巼
+3. **璇姤澶勭悊**锛氬棰戠箒璇姤鐨勮溅杈嗗彲鍒涘缓涓撳睘閰嶇疆
+4. **鏁版嵁鍒嗘瀽**锛氬鍑烘暟鎹繘琛岃秼鍔垮垎鏋愬拰鏁堟灉璇勪及
+
+### 鎬ц兘寤鸿
+1. **绱㈠紩缁存姢**锛氬畾鏈熸鏌ュ拰浼樺寲鏁版嵁搴撶储寮�
+2. **鏁版嵁娓呯悊**锛氬缓璁繚鐣�3涓湀鐨勫憡璀﹁褰曪紝瀹氭湡褰掓。鍘嗗彶鏁版嵁
+3. **鏃ュ織绾у埆**锛氱敓浜х幆澧冧娇鐢↖NFO绾у埆锛屽噺灏戞棩蹇楄緭鍑�
+4. **鐩戞帶棰戠巼**锛氳溅杈嗘暟閲忓ぇ鏃跺彲閫傚綋寤堕暱鎵ц闂撮殧
+
+## 宸茬煡闄愬埗
+
+1. **GPS鏁版嵁渚濊禆**锛氫緷璧朑PS鍒嗘閲岀▼璁$畻浠诲姟姝e父杩愯
+2. **閫氱煡鏂瑰紡**锛氱洰鍓嶄粎鏀寔浼佷笟寰俊锛屽皬绋嬪簭閫氱煡闇�瑕佽繘涓�姝ュ紑鍙�
+3. **閰嶇疆鐣岄潰**锛氬憡璀﹂厤缃〃鏆傛棤鍓嶇绠$悊鐣岄潰锛岄渶閫氳繃鏁版嵁搴撴搷浣�
+4. **缁熻鎶ヨ〃**锛氭殏鏃犲浘琛ㄥ寲鐨勭粺璁″垎鏋愬姛鑳�
+
+## 鍚庣画浼樺寲鏂瑰悜
+
+### 鐭湡浼樺寲锛�1-2鍛級
+1. [ ] 娣诲姞鍛婅閰嶇疆绠$悊鐣岄潰
+2. [ ] 浼樺寲閫氱煡娑堟伅妯℃澘
+3. [ ] 娣诲姞鍛婅缁熻鍥捐〃
+4. [ ] 瀹屽杽鍓嶇绠$悊椤甸潰
+
+### 涓湡浼樺寲锛�1-2鏈堬級
+1. [ ] 鏀寔澶氱鍛婅绫诲瀷锛堣秴閫熴�侀暱鏃堕棿鍋滆溅绛夛級
+2. [ ] 瀹炵幇鍛婅瑙勫垯寮曟搸
+3. [ ] 娣诲姞绉诲姩绔帹閫�
+4. [ ] 鏀寔鍛婅鍗囩骇鏈哄埗
+
+### 闀挎湡浼樺寲锛�3-6鏈堬級
+1. [ ] 鏅鸿兘鍛婅鎺ㄨ崘
+2. [ ] 鍛婅瓒嬪娍棰勬祴
+3. [ ] 鑷姩鍖栧鐞嗗缓璁�
+4. [ ] 澶ф暟鎹垎鏋愭姤鍛�
+
+## 鎶�鏈敮鎸�
+
+濡傛湁闂鎴栧缓璁紝璇峰弬鑰冿細
+- **鍔熻兘鏂囨。**锛歞oc/杞﹁締寮傚父杩愯鐩戞帶鍛婅鍔熻兘璇存槑.md
+- **閮ㄧ讲鎸囧崡**锛歞oc/杞﹁締寮傚父杩愯鐩戞帶鍛婅-蹇�熼儴缃叉寚鍗�.md
+- **绯荤粺鏃ュ織**锛歭ogs/sys-info.log
+- **浠诲姟鏃ュ織**锛氱郴缁熺洃鎺� > 璋冨害鏃ュ織
+
+## 鐗堟湰淇℃伅
+
+- **鐗堟湰鍙�**锛歏1.0.0
+- **鍙戝竷鏃ユ湡**锛�2026-01-12
+- **寮�鍙戞ā寮�**锛氬叏閲忎氦浠�
+- **娴嬭瘯鐘舵��**锛氬緟娴嬭瘯楠岃瘉
+- **鐢熶骇鐘舵��**锛氬緟閮ㄧ讲涓婄嚎
+
+---
+
+**瀹炵幇璇存槑**锛氭湰鍔熻兘宸插畬鏁村疄鐜版墍鏈夋牳蹇冩ā鍧楋紝鍖呮嫭鏁版嵁搴撹璁°�佸悗绔唬鐮併�佸畾鏃朵换鍔°�佺郴缁熼泦鎴愬拰瀹屾暣鏂囨。銆傛墍鏈変唬鐮佸潎宸插垱寤哄苟淇濆瓨鍒扮浉搴旀枃浠讹紝鍙洿鎺ラ儴缃蹭娇鐢ㄣ��
diff --git "a/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-\345\277\253\351\200\237\351\203\250\347\275\262\346\214\207\345\215\227.md" "b/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-\345\277\253\351\200\237\351\203\250\347\275\262\346\214\207\345\215\227.md"
new file mode 100644
index 0000000..e5547dc
--- /dev/null
+++ "b/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-\345\277\253\351\200\237\351\203\250\347\275\262\346\214\207\345\215\227.md"
@@ -0,0 +1,262 @@
+# 杞﹁締寮傚父杩愯鐩戞帶鍛婅鍔熻兘 - 蹇�熼儴缃叉寚鍗�
+
+## 鍔熻兘姒傝堪
+鐩戞帶杞﹁締鏃犱换鍔¤繍琛岃秴杩囬厤缃叕閲屾暟锛堥粯璁�10鍏噷锛夋椂鑷姩浜х敓鍛婅锛屽苟閫氳繃灏忕▼搴�/浼佷笟寰俊閫氱煡鐩稿叧璐熻矗浜恒��
+
+## 蹇�熼儴缃诧紙5姝ュ畬鎴愶級
+
+### 绗�1姝ワ細鎵ц鏁版嵁搴撹剼鏈�
+```bash
+cd /path/to/RuoYi-Vue-master
+mysql -u鐢ㄦ埛鍚� -p瀵嗙爜 鏁版嵁搴撳悕 < sql/vehicle_abnormal_alert.sql
+```
+
+**鑴氭湰鍐呭鍖呮嫭**锛�
+- 鉁� 鍒涘缓鍛婅璁板綍琛� (tb_vehicle_abnormal_alert)
+- 鉁� 鍒涘缓鍛婅閰嶇疆琛� (tb_vehicle_alert_config)  
+- 鉁� 鎻掑叆绯荤粺閰嶇疆鍙傛暟 (6涓厤缃」)
+- 鉁� 鍒涘缓瀹氭椂浠诲姟 (vehicle Abnormal Alert Task)
+- 鉁� 鍒涘缓鑿滃崟鏉冮檺 (杞﹁締寮傚父鍛婅绠$悊)
+
+### 绗�2姝ワ細閰嶇疆閫氱煡鐢ㄦ埛
+鍦�"绯荤粺绠$悊 > 鍙傛暟璁剧疆"涓厤缃互涓嬪弬鏁帮細
+
+| 鍙傛暟鍚嶇О | 鍙傛暟閿悕 | 閰嶇疆鍊肩ず渚� | 璇存槑 |
+|---------|---------|-----------|------|
+| 杞﹁締寮傚父鍛婅閫氱煡鐢ㄦ埛 | vehicle.alert.notify.users | 1,2,3 | 鎺ユ敹閫氱煡鐨勭敤鎴稩D锛岄�楀彿鍒嗛殧 |
+
+> 馃挕 **鎻愮ず**锛氬鏋滀笉閰嶇疆锛屽皢灏濊瘯鏍规嵁杞﹁締褰掑睘閮ㄩ棬鏌ユ壘璐熻矗浜�
+
+### 绗�3姝ワ細鍚敤瀹氭椂浠诲姟
+1. 鐧诲綍鍚庡彴绠$悊绯荤粺
+2. 杩涘叆"绯荤粺鐩戞帶 > 瀹氭椂浠诲姟"
+3. 鎵惧埌"杞﹁締寮傚父杩愯鐩戞帶浠诲姟"
+4. 鐐瑰嚮鐘舵�佸紑鍏筹紝灏嗗叾璁剧疆涓�"杩愯涓�"
+
+### 绗�4姝ワ細閰嶇疆浼佷笟寰俊锛堝彲閫夛級
+濡傞渶浣跨敤浼佷笟寰俊閫氱煡鍔熻兘锛屽湪"绯荤粺绠$悊 > 鍙傛暟璁剧疆"涓‘淇濅互涓嬮厤缃細
+
+| 鍙傛暟閿悕 | 璇存槑 | 绀轰緥鍊� |
+|---------|------|--------|
+| qy_wechat.enable | 浼佷笟寰俊鍚敤寮�鍏� | true |
+| qy_wechat.corp_id | 浼佷笟ID | ww123456789 |
+| qy_wechat.corp_secret | 搴旂敤Secret | xxxxx |
+| qy_wechat.agent_id | 搴旂敤ID | 1000002 |
+
+### 绗�5姝ワ細楠岃瘉鍔熻兘
+1. 纭繚GPS鍒嗘閲岀▼璁$畻浠诲姟姝e父杩愯
+2. 鏌ョ湅"杞﹁締绠$悊 > 杞﹁締寮傚父鍛婅"鑿滃崟鏄惁鏄剧ず
+3. 瑙傚療瀹氭椂浠诲姟鎵ц鏃ュ織
+4. 娴嬭瘯鍛婅鐢熸垚鍜岄�氱煡鍙戦��
+
+## 鏍稿績閰嶇疆鍙傛暟璇存槑
+
+### 蹇呴渶閰嶇疆
+| 閰嶇疆閿� | 榛樿鍊� | 璇存槑 |
+|-------|--------|------|
+| vehicle.alert.enabled | true | 鍔熻兘鎬诲紑鍏筹紝false鍒欏仠鐢� |
+| vehicle.alert.mileage.threshold | 10 | 鍛婅闃堝�硷紙鍏噷锛� |
+
+### 鍙�夐厤缃�
+| 閰嶇疆閿� | 榛樿鍊� | 璇存槑 |
+|-------|--------|------|
+| vehicle.alert.daily.limit | 5 | 姣忚溅姣忓ぉ鏈�澶氬憡璀︽鏁� |
+| vehicle.alert.interval.minutes | 5 | 涓ゆ鍛婅鏈�灏忛棿闅旓紙鍒嗛挓锛� |
+| vehicle.alert.time.window | 10 | 鐩戞帶鏃堕棿绐楀彛锛堝垎閽燂級 |
+| vehicle.alert.notify.users | 锛堢┖锛� | 閫氱煡鐢ㄦ埛ID鍒楄〃 |
+
+## 瀹氭椂浠诲姟璇存槑
+
+### 浠诲姟閰嶇疆
+- **浠诲姟鍚嶇О**锛氳溅杈嗗紓甯歌繍琛岀洃鎺т换鍔�  
+- **鎵ц棰戠巼**锛歚0 */5 * * * ?` (姣�5鍒嗛挓鎵ц涓�娆�)
+- **璋冪敤鏂规硶**锛歚vehicleAbnormalAlertTask.monitorVehicleAbnormalRunning()`
+- **骞跺彂鎺у埗**锛氱姝㈠苟鍙�
+- **榛樿鐘舵��**锛氭殏鍋滐紙闇�鎵嬪姩鍚敤锛�
+
+### 鐩戞帶閫昏緫
+```
+姣�5鍒嗛挓鎵ц涓�娆�
+  鈫�
+鏌ヨ鎵�鏈夎溅杈�
+  鈫�
+閫愯溅妫�鏌ワ紙骞惰澶勭悊锛�
+  鈹溾攢 妫�鏌ユ槸鍚︽湁姝e湪鎵ц鐨勪换鍔�
+  鈹溾攢 璁$畻鏈�杩�10鍒嗛挓杩愯閲岀▼
+  鈹溾攢 鍒ゆ柇鏄惁瓒呰繃闃堝��
+  鈹溾攢 妫�鏌ュ憡璀﹂鐜囬檺鍒�
+  鈹斺攢 鍒涘缓鍛婅骞跺彂閫侀�氱煡
+```
+
+## 鏉冮檺閰嶇疆
+
+### 鑿滃崟鏉冮檺
+- **鐖惰彍鍗�**锛氳溅杈嗙鐞�
+- **鑿滃崟鍚嶇О**锛氳溅杈嗗紓甯稿憡璀�
+- **鏉冮檺鏍囪瘑**锛歴ystem:vehicleAlert:list
+
+### 鎸夐挳鏉冮檺
+- `system:vehicleAlert:query` - 鏌ヨ鍛婅
+- `system:vehicleAlert:detail` - 鍛婅璇︽儏
+- `system:vehicleAlert:handle` - 澶勭悊鍛婅
+- `system:vehicleAlert:export` - 瀵煎嚭鏁版嵁
+- `system:vehicleAlert:config` - 閰嶇疆绠$悊
+
+## 甯歌闂
+
+### Q1: 鍛婅涓嶇敓鎴愶紵
+**鎺掓煡姝ラ**锛�
+1. 妫�鏌ュ畾鏃朵换鍔℃槸鍚﹀惎鐢�
+2. 鏌ョ湅瀹氭椂浠诲姟鎵ц鏃ュ織锛歚绯荤粺鐩戞帶 > 璋冨害鏃ュ織`
+3. 纭 `vehicle.alert.enabled = true`
+4. 纭GPS鍒嗘閲岀▼琛ㄦ湁鏁版嵁
+5. 妫�鏌ヨ溅杈嗘槸鍚︾湡鐨勬棤浠诲姟杩愯
+
+### Q2: 閫氱煡涓嶅彂閫侊紵
+**鎺掓煡姝ラ**锛�
+1. 妫�鏌ヤ紒涓氬井淇¢厤缃槸鍚︽纭�
+2. 纭 `qy_wechat.enable = true`
+3. 鏌ョ湅鍚庡彴鏃ュ織锛氭悳绱�"鍙戦�佸憡璀﹂�氱煡"
+4. 纭鐢ㄦ埛宸茬粦瀹氫紒涓氬井淇D
+
+### Q3: 鍛婅澶绻侊紵
+**瑙e喅鏂规**锛�
+1. 璋冮珮鍏噷鏁伴槇鍊硷細`vehicle.alert.mileage.threshold = 20`
+2. 鍑忓皯姣忔棩鍛婅娆℃暟锛歚vehicle.alert.daily.limit = 3`
+3. 澧炲姞鍛婅闂撮殧锛歚vehicle.alert.interval.minutes = 10`
+
+### Q4: GPS閲岀▼鏁版嵁涓嶅噯锛�
+**妫�鏌ラ」**锛�
+1. 纭GPS鍒嗘閲岀▼璁$畻浠诲姟姝e父杩愯
+2. 鏌ョ湅 `tb_vehicle_gps_segment_mileage` 琛ㄦ暟鎹�
+3. 璋冩暣鐩戞帶鏃堕棿绐楀彛锛歚vehicle.alert.time.window = 15`
+
+## 娴嬭瘯鎸囧崡
+
+### 娴嬭瘯姝ラ
+1. **鍑嗗娴嬭瘯鏁版嵁**
+   - 纭繚鏈夎溅杈咷PS鏁版嵁
+   - 纭繚GPS鍒嗘閲岀▼璁$畻浠诲姟宸茶繍琛�
+   - 閫夋嫨涓�杈嗘棤浠诲姟鐨勮溅杈�
+
+2. **璋冩暣閰嶇疆渚夸簬瑙﹀彂**
+   ```sql
+   UPDATE sys_config SET config_value = '1' 
+   WHERE config_key = 'vehicle.alert.mileage.threshold';
+   ```
+
+3. **鎵嬪姩瑙﹀彂瀹氭椂浠诲姟**
+   - 杩涘叆"绯荤粺鐩戞帶 > 瀹氭椂浠诲姟"
+   - 鎵惧埌"杞﹁締寮傚父杩愯鐩戞帶浠诲姟"
+   - 鐐瑰嚮"鎵ц涓�娆�"鎸夐挳
+
+4. **鏌ョ湅鎵ц缁撴灉**
+   - 鏌ョ湅"绯荤粺鐩戞帶 > 璋冨害鏃ュ織"
+   - 鏌ョ湅"杞﹁締绠$悊 > 杞﹁締寮傚父鍛婅"鍒楄〃
+   - 妫�鏌ユ槸鍚︽敹鍒颁紒涓氬井淇¢�氱煡
+
+5. **鎭㈠閰嶇疆**
+   ```sql
+   UPDATE sys_config SET config_value = '10' 
+   WHERE config_key = 'vehicle.alert.mileage.threshold';
+   ```
+
+## 鎬ц兘浼樺寲寤鸿
+
+### 鏁版嵁搴撶储寮曪紙宸茶嚜鍔ㄥ垱寤猴級
+```sql
+-- 鍛婅璁板綍琛ㄧ储寮�
+CREATE INDEX idx_vehicle_id ON tb_vehicle_abnormal_alert(vehicle_id);
+CREATE INDEX idx_alert_date ON tb_vehicle_abnormal_alert(alert_date);
+CREATE INDEX idx_vehicle_date ON tb_vehicle_abnormal_alert(vehicle_id, alert_date);
+CREATE INDEX idx_status ON tb_vehicle_abnormal_alert(status);
+```
+
+### 鐩戞帶寤鸿
+- **杞﹁締鏁� < 100**锛氫娇鐢ㄩ粯璁ら厤缃�
+- **杞﹁締鏁� 100-500**锛氳�冭檻澧炲姞鏃堕棿绐楀彛涓�15鍒嗛挓
+- **杞﹁締鏁� > 500**锛氳�冭檻澧炲姞鎵ц闂撮殧涓�10鍒嗛挓
+
+### 鏃ュ織绾у埆
+鍦ㄧ敓浜х幆澧冿紝寤鸿璁剧疆鏃ュ織绾у埆涓� INFO锛�
+```yaml
+logging:
+  level:
+    com.ruoyi.quartz.task.VehicleAbnormalAlertTask: INFO
+```
+
+## 鏁呴殰鎺掓煡
+
+### 鏃ュ織浣嶇疆
+- **搴旂敤鏃ュ織**锛歚logs/sys-info.log`
+- **瀹氭椂浠诲姟鏃ュ織**锛氭暟鎹簱琛� `sys_job_log`
+
+### 鍏抽敭鏃ュ織鎼滅储
+```bash
+# 鐩戞帶浠诲姟鎵ц鏃ュ織
+grep "杞﹁締寮傚父杩愯鐩戞帶" logs/sys-info.log
+
+# 鍛婅鐢熸垚鏃ュ織
+grep "浜х敓寮傚父鍛婅" logs/sys-info.log
+
+# 閫氱煡鍙戦�佹棩蹇�
+grep "鍙戦�佸憡璀﹂�氱煡" logs/sys-info.log
+```
+
+### 鏁版嵁搴撴鏌�
+```sql
+-- 鏌ョ湅浠婃棩鍛婅缁熻
+SELECT status, COUNT(*) as count 
+FROM tb_vehicle_abnormal_alert 
+WHERE DATE(alert_date) = CURDATE()
+GROUP BY status;
+
+-- 鏌ョ湅鍛婅棰戠箒鐨勮溅杈�
+SELECT vehicle_no, COUNT(*) as alert_count
+FROM tb_vehicle_abnormal_alert
+WHERE DATE(alert_date) = CURDATE()
+GROUP BY vehicle_no
+ORDER BY alert_count DESC
+LIMIT 10;
+
+-- 鏌ョ湅瀹氭椂浠诲姟鎵ц鎯呭喌
+SELECT job_name, status, job_message, create_time
+FROM sys_job_log
+WHERE job_name = '杞﹁締寮傚父杩愯鐩戞帶浠诲姟'
+ORDER BY create_time DESC
+LIMIT 10;
+```
+
+## 鎶�鏈敮鎸�
+
+濡傞亣鍒伴棶棰橈細
+1. 鏌ョ湅鏈枃妗g殑"甯歌闂"閮ㄥ垎
+2. 妫�鏌ョ郴缁熸棩蹇楀拰瀹氭椂浠诲姟鏃ュ織
+3. 鏌ヨ鏁版嵁搴撹〃纭鏁版嵁鐘舵��
+4. 鑱旂郴鎶�鏈敮鎸佸洟闃�
+
+## 鏂囦欢娓呭崟
+
+| 鏂囦欢绫诲瀷 | 鏂囦欢璺緞 | 璇存槑 |
+|---------|---------|------|
+| SQL鑴氭湰 | sql/vehicle_abnormal_alert.sql | 鏁版嵁搴撳垵濮嬪寲鑴氭湰 |
+| 瀹炰綋绫� | ruoyi-system/.../domain/VehicleAbnormalAlert.java | 鍛婅瀹炰綋 |
+| Mapper | ruoyi-system/.../mapper/VehicleAbnormalAlertMapper.java | 鏁版嵁璁块棶 |
+| Service | ruoyi-system/.../service/impl/VehicleAbnormalAlertServiceImpl.java | 涓氬姟閫昏緫 |
+| Controller | ruoyi-admin/.../controller/system/VehicleAbnormalAlertController.java | 鎺ュ彛鎺у埗鍣� |
+| 瀹氭椂浠诲姟 | ruoyi-quartz/.../task/VehicleAbnormalAlertTask.java | 鐩戞帶浠诲姟 |
+| 閰嶇疆鏂囨。 | doc/杞﹁締寮傚父杩愯鐩戞帶鍛婅鍔熻兘璇存槑.md | 瀹屾暣鏂囨。 |
+| 閮ㄧ讲鎸囧崡 | 鏈枃妗� | 蹇�熼儴缃� |
+
+---
+
+**閮ㄧ讲瀹屾垚鍚�**锛岃纭锛�
+- [  ] 鏁版嵁搴撹〃鍒涘缓鎴愬姛
+- [  ] 鑿滃崟鏄剧ず姝e父
+- [  ] 瀹氭椂浠诲姟宸插惎鐢�
+- [  ] 閰嶇疆鍙傛暟宸茶缃�
+- [  ] 娴嬭瘯鍛婅鐢熸垚鎴愬姛
+- [  ] 閫氱煡鍙戦�佹甯�
+
+**鐗堟湰**: V1.0.0  
+**鏇存柊鏃ユ湡**: 2026-01-12
diff --git "a/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-\350\217\234\345\215\225\351\205\215\347\275\256\350\257\264\346\230\216.md" "b/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-\350\217\234\345\215\225\351\205\215\347\275\256\350\257\264\346\230\216.md"
new file mode 100644
index 0000000..5845b52
--- /dev/null
+++ "b/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246-\350\217\234\345\215\225\351\205\215\347\275\256\350\257\264\346\230\216.md"
@@ -0,0 +1,350 @@
+# 杞﹁締寮傚父杩愯鐩戞帶鍛婅 - 鑿滃崟閰嶇疆璇存槑
+
+## 馃搵 姒傝堪
+
+鏈枃妗h缁嗚鏄庤溅杈嗗紓甯歌繍琛岀洃鎺у憡璀﹀姛鑳界殑鍚庡彴鑿滃崟缁撴瀯鍜屾潈闄愰厤缃�傛墽琛孲QL鑴氭湰鍚庝細鑷姩鍒涘缓瀹屾暣鐨勮彍鍗曠粨鏋勩��
+
+## 馃幆 鑿滃崟缁撴瀯
+
+### 涓�绾ц彍鍗曪細杞﹁締鐩戞帶
+
+```
+杞﹁締鐩戞帶 (vehicle-monitor) - 鐩綍鑿滃崟
+鈹溾攢鈹� 杞﹁締寮傚父鍛婅 (vehicleAlert) - 鑿滃崟
+鈹�   鈹溾攢鈹� 鍛婅鏌ヨ - 鎸夐挳鏉冮檺
+鈹�   鈹溾攢鈹� 澶勭悊鍛婅 - 鎸夐挳鏉冮檺
+鈹�   鈹溾攢鈹� 鍒犻櫎鍛婅 - 鎸夐挳鏉冮檺
+鈹�   鈹斺攢鈹� 瀵煎嚭鍛婅 - 鎸夐挳鏉冮檺
+鈹斺攢鈹� 鍛婅閰嶇疆绠$悊 (vehicleAlertConfig) - 鑿滃崟
+    鈹溾攢鈹� 閰嶇疆鏌ヨ - 鎸夐挳鏉冮檺
+    鈹溾攢鈹� 鏂板閰嶇疆 - 鎸夐挳鏉冮檺
+    鈹溾攢鈹� 淇敼閰嶇疆 - 鎸夐挳鏉冮檺
+    鈹溾攢鈹� 鍒犻櫎閰嶇疆 - 鎸夐挳鏉冮檺
+    鈹斺攢鈹� 瀵煎嚭閰嶇疆 - 鎸夐挳鏉冮檺
+```
+
+## 馃搳 璇︾粏閰嶇疆
+
+### 1. 杞﹁締鐩戞帶锛堢埗鑿滃崟锛�
+
+| 瀛楁 | 鍊� |
+|------|-----|
+| 鑿滃崟鍚嶇О | 杞﹁締鐩戞帶 |
+| 鐖惰彍鍗� | 椤剁骇鑿滃崟 (0) |
+| 鏄剧ず鎺掑簭 | 5 |
+| 璺敱鍦板潃 | vehicle-monitor |
+| 鑿滃崟绫诲瀷 | 鐩綍 (M) |
+| 鑿滃崟鍥炬爣 | monitor |
+| 鏄惁鍙 | 鏄� |
+| 鑿滃崟鐘舵�� | 姝e父 |
+| 澶囨敞 | 杞﹁締鐩戞帶绠$悊鐩綍 |
+
+**鐗圭偣**锛�
+- 馃搧 鐩綍绫诲瀷锛屼笉瀵瑰簲鍏蜂綋椤甸潰
+- 馃帹 浣跨敤monitor鍥炬爣
+- 馃搷 椤剁骇鑿滃崟锛屾樉绀哄湪宸︿晶瀵艰埅鏍�
+
+### 2. 杞﹁締寮傚父鍛婅锛堝瓙鑿滃崟锛�
+
+| 瀛楁 | 鍊� |
+|------|-----|
+| 鑿滃崟鍚嶇О | 杞﹁締寮傚父鍛婅 |
+| 鐖惰彍鍗� | 杞﹁締鐩戞帶 |
+| 鏄剧ず鎺掑簭 | 1 |
+| 璺敱鍦板潃 | vehicleAlert |
+| 缁勪欢璺緞 | system/vehicleAlert/index |
+| 鑿滃崟绫诲瀷 | 鑿滃崟 (C) |
+| 鑿滃崟鍥炬爣 | warning |
+| 鏄惁缂撳瓨 | 鍚� |
+| 鏉冮檺鏍囪瘑 | system:vehicleAlert:list |
+| 澶囨敞 | 杞﹁締寮傚父杩愯鍛婅绠$悊 |
+
+**鍔熻兘鎸夐挳鏉冮檺**锛�
+
+#### 2.1 鍛婅鏌ヨ
+- **鏉冮檺鏍囪瘑**: `system:vehicleAlert:query`
+- **鐢ㄩ��**: 鏌ヨ鍛婅鍒楄〃鍜岃鎯�
+
+#### 2.2 澶勭悊鍛婅
+- **鏉冮檺鏍囪瘑**: `system:vehicleAlert:handle`
+- **鐢ㄩ��**: 澶勭悊鍗曟潯鎴栨壒閲忓鐞嗗憡璀�
+
+#### 2.3 鍒犻櫎鍛婅
+- **鏉冮檺鏍囪瘑**: `system:vehicleAlert:remove`
+- **鐢ㄩ��**: 鍒犻櫎鍛婅璁板綍
+
+#### 2.4 瀵煎嚭鍛婅
+- **鏉冮檺鏍囪瘑**: `system:vehicleAlert:export`
+- **鐢ㄩ��**: 瀵煎嚭鍛婅鏁版嵁涓篍xcel
+
+### 3. 鍛婅閰嶇疆绠$悊锛堝瓙鑿滃崟锛�
+
+| 瀛楁 | 鍊� |
+|------|-----|
+| 鑿滃崟鍚嶇О | 鍛婅閰嶇疆绠$悊 |
+| 鐖惰彍鍗� | 杞﹁締鐩戞帶 |
+| 鏄剧ず鎺掑簭 | 2 |
+| 璺敱鍦板潃 | vehicleAlertConfig |
+| 缁勪欢璺緞 | system/vehicleAlertConfig/index |
+| 鑿滃崟绫诲瀷 | 鑿滃崟 (C) |
+| 鑿滃崟鍥炬爣 | edit |
+| 鏄惁缂撳瓨 | 鍚� |
+| 鏉冮檺鏍囪瘑 | system:vehicleAlertConfig:list |
+| 澶囨敞 | 杞﹁締鍛婅閰嶇疆绠$悊 |
+
+**鍔熻兘鎸夐挳鏉冮檺**锛�
+
+#### 3.1 閰嶇疆鏌ヨ
+- **鏉冮檺鏍囪瘑**: `system:vehicleAlertConfig:query`
+- **鐢ㄩ��**: 鏌ヨ閰嶇疆鍒楄〃鍜岃鎯�
+
+#### 3.2 鏂板閰嶇疆
+- **鏉冮檺鏍囪瘑**: `system:vehicleAlertConfig:add`
+- **鐢ㄩ��**: 鏂板鍏ㄥ眬/閮ㄩ棬/杞﹁締閰嶇疆
+
+#### 3.3 淇敼閰嶇疆
+- **鏉冮檺鏍囪瘑**: `system:vehicleAlertConfig:edit`
+- **鐢ㄩ��**: 淇敼宸叉湁閰嶇疆
+
+#### 3.4 鍒犻櫎閰嶇疆
+- **鏉冮檺鏍囪瘑**: `system:vehicleAlertConfig:remove`
+- **鐢ㄩ��**: 鍒犻櫎閰嶇疆璁板綍
+
+#### 3.5 瀵煎嚭閰嶇疆
+- **鏉冮檺鏍囪瘑**: `system:vehicleAlertConfig:export`
+- **鐢ㄩ��**: 瀵煎嚭閰嶇疆鏁版嵁涓篍xcel
+
+## 馃攼 鏉冮檺鏍囪瘑鍒楄〃
+
+### 杞﹁締寮傚父鍛婅妯″潡
+
+| 鏉冮檺鏍囪瘑 | 璇存槑 | 绫诲瀷 |
+|---------|------|------|
+| `system:vehicleAlert:list` | 鍛婅鍒楄〃 | 鑿滃崟璁块棶 |
+| `system:vehicleAlert:query` | 鍛婅鏌ヨ | 鎸夐挳鏉冮檺 |
+| `system:vehicleAlert:handle` | 澶勭悊鍛婅 | 鎸夐挳鏉冮檺 |
+| `system:vehicleAlert:remove` | 鍒犻櫎鍛婅 | 鎸夐挳鏉冮檺 |
+| `system:vehicleAlert:export` | 瀵煎嚭鍛婅 | 鎸夐挳鏉冮檺 |
+
+### 鍛婅閰嶇疆绠$悊妯″潡
+
+| 鏉冮檺鏍囪瘑 | 璇存槑 | 绫诲瀷 |
+|---------|------|------|
+| `system:vehicleAlertConfig:list` | 閰嶇疆鍒楄〃 | 鑿滃崟璁块棶 |
+| `system:vehicleAlertConfig:query` | 閰嶇疆鏌ヨ | 鎸夐挳鏉冮檺 |
+| `system:vehicleAlertConfig:add` | 鏂板閰嶇疆 | 鎸夐挳鏉冮檺 |
+| `system:vehicleAlertConfig:edit` | 淇敼閰嶇疆 | 鎸夐挳鏉冮檺 |
+| `system:vehicleAlertConfig:remove` | 鍒犻櫎閰嶇疆 | 鎸夐挳鏉冮檺 |
+| `system:vehicleAlertConfig:export` | 瀵煎嚭閰嶇疆 | 鎸夐挳鏉冮檺 |
+
+## 馃殌 SQL鑴氭湰璇存槑
+
+### 鑴氭湰鐗圭偣
+
+1. **闃查噸澶嶆墽琛�**: 浣跨敤 `NOT EXISTS` 妫�鏌ワ紝閬垮厤閲嶅鎻掑叆
+2. **鍔ㄦ�佽幏鍙朓D**: 浣跨敤鍙橀噺瀛樺偍鐖惰彍鍗旾D
+3. **椤哄簭鎵ц**: 鍏堝垱寤虹埗鑿滃崟锛屽啀鍒涘缓瀛愯彍鍗曞拰鎸夐挳
+4. **瀹屾暣鎬�**: 涓�娆℃�у垱寤烘墍鏈夎彍鍗曞拰鏉冮檺
+
+### 鍏抽敭SQL璇彞
+
+#### 1. 鍒涘缓鐖惰彍鍗曪紙闃查噸澶嶏級
+```sql
+INSERT INTO sys_menu (...)
+SELECT '杞﹁締鐩戞帶', 0, 5, 'vehicle-monitor', ...
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_name = '杞﹁締鐩戞帶' AND menu_type = 'M');
+```
+
+#### 2. 鑾峰彇鐖惰彍鍗旾D
+```sql
+SET @vehicleMonitorMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = '杞﹁締鐩戞帶' AND menu_type = 'M' LIMIT 1);
+```
+
+#### 3. 鍒涘缓瀛愯彍鍗�
+```sql
+INSERT INTO sys_menu (...)
+SELECT '杞﹁締寮傚父鍛婅', @vehicleMonitorMenuId, 1, 'vehicleAlert', ...
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_name = '杞﹁締寮傚父鍛婅' AND perms = 'system:vehicleAlert:list');
+```
+
+## 馃帹 鍓嶇璺敱閰嶇疆
+
+濡傛灉浣跨敤鍔ㄦ�佽矾鐢憋紙浠庡悗绔姞杞借彍鍗曪級锛屾棤闇�棰濆閰嶇疆銆傚鏋滀娇鐢ㄩ潤鎬佽矾鐢憋紝闇�鍦� `router/index.js` 涓坊鍔狅細
+
+```javascript
+{
+  path: '/vehicle-monitor',
+  component: Layout,
+  redirect: '/vehicle-monitor/vehicleAlert',
+  name: 'VehicleMonitor',
+  meta: { title: '杞﹁締鐩戞帶', icon: 'monitor' },
+  children: [
+    {
+      path: 'vehicleAlert',
+      component: () => import('@/views/system/vehicleAlert/index'),
+      name: 'VehicleAlert',
+      meta: { title: '杞﹁締寮傚父鍛婅', icon: 'warning' }
+    },
+    {
+      path: 'vehicleAlertConfig',
+      component: () => import('@/views/system/vehicleAlertConfig/index'),
+      name: 'VehicleAlertConfig',
+      meta: { title: '鍛婅閰嶇疆绠$悊', icon: 'edit' }
+    }
+  ]
+}
+```
+
+## 馃懃 瑙掕壊鏉冮檺鍒嗛厤
+
+### 绠$悊鍛樿鑹诧紙admin锛�
+寤鸿鍒嗛厤鎵�鏈夋潈闄愶細
+- 鉁� 鍛婅鍒楄〃璁块棶
+- 鉁� 鍛婅鏌ヨ
+- 鉁� 澶勭悊鍛婅
+- 鉁� 鍒犻櫎鍛婅
+- 鉁� 瀵煎嚭鍛婅
+- 鉁� 閰嶇疆绠$悊鎵�鏈夋潈闄�
+
+### 鏅�氱敤鎴疯鑹诧紙user锛�
+寤鸿鍒嗛厤鍩虹鏉冮檺锛�
+- 鉁� 鍛婅鍒楄〃璁块棶
+- 鉁� 鍛婅鏌ヨ
+- 鉁� 澶勭悊鍛婅
+- 鉂� 鍒犻櫎鍛婅
+- 鉁� 瀵煎嚭鍛婅
+- 鉂� 閰嶇疆绠$悊鏉冮檺
+
+### 鏌ョ湅鑰呰鑹诧紙viewer锛�
+寤鸿鍒嗛厤鍙鏉冮檺锛�
+- 鉁� 鍛婅鍒楄〃璁块棶
+- 鉁� 鍛婅鏌ヨ
+- 鉂� 澶勭悊鍛婅
+- 鉂� 鍒犻櫎鍛婅
+- 鉁� 瀵煎嚭鍛婅
+- 鉂� 閰嶇疆绠$悊鏉冮檺
+
+## 馃敡 瑙掕壊鍒嗛厤姝ラ
+
+### 鏂瑰紡涓�锛氶�氳繃鍚庡彴绠$悊鐣岄潰
+
+1. 鐧诲綍鍚庡彴绯荤粺
+2. 杩涘叆 **绯荤粺绠$悊 > 瑙掕壊绠$悊**
+3. 閫夋嫨瑕佸垎閰嶆潈闄愮殑瑙掕壊锛岀偣鍑�"淇敼"
+4. 鍦�"鑿滃崟鏉冮檺"涓嬀閫夐渶瑕佺殑鑿滃崟鍜屾寜閽�
+5. 鐐瑰嚮"纭畾"淇濆瓨
+
+### 鏂瑰紡浜岋細閫氳繃SQL鐩存帴鍒嗛厤
+
+```sql
+-- 涓鸿鑹睮D=2锛堢ず渚嬶級鍒嗛厤鍛婅绠$悊鏉冮檺
+INSERT INTO sys_role_menu (role_id, menu_id)
+SELECT 2, menu_id FROM sys_menu 
+WHERE perms LIKE 'system:vehicleAlert:%' OR perms LIKE 'system:vehicleAlertConfig:%';
+```
+
+## 馃搵 楠岃瘉娓呭崟
+
+鎵цSQL鑴氭湰鍚庯紝璇烽獙璇佷互涓嬪唴瀹癸細
+
+### 1. 鑿滃崟鍒涘缓楠岃瘉
+```sql
+-- 鏌ヨ杞﹁締鐩戞帶鑿滃崟缁撴瀯
+SELECT 
+    m1.menu_name AS '涓�绾ц彍鍗�',
+    m2.menu_name AS '浜岀骇鑿滃崟',
+    m3.menu_name AS '鎸夐挳',
+    m3.perms AS '鏉冮檺鏍囪瘑'
+FROM sys_menu m1
+LEFT JOIN sys_menu m2 ON m2.parent_id = m1.menu_id
+LEFT JOIN sys_menu m3 ON m3.parent_id = m2.menu_id
+WHERE m1.menu_name = '杞﹁締鐩戞帶'
+ORDER BY m2.order_num, m3.order_num;
+```
+
+**棰勬湡缁撴灉**锛氬簲璇ョ湅鍒板畬鏁寸殑鑿滃崟鏍戠粨鏋�
+
+### 2. 鏉冮檺鏁伴噺楠岃瘉
+```sql
+-- 缁熻鍛婅鐩稿叧鏉冮檺鏁伴噺
+SELECT COUNT(*) AS '鏉冮檺鏁伴噺' FROM sys_menu 
+WHERE perms LIKE 'system:vehicleAlert%';
+```
+
+**棰勬湡缁撴灉**锛氬簲璇ユ湁11涓潈闄愶紙2涓猯ist + 9涓寜閽級
+
+### 3. 鍓嶇璁块棶楠岃瘉
+- [ ] 鐧诲綍绯荤粺鍚庤兘鐪嬪埌"杞﹁締鐩戞帶"鑿滃崟
+- [ ] 鐐瑰嚮"杞﹁締寮傚父鍛婅"鑳芥甯歌闂〉闈�
+- [ ] 鐐瑰嚮"鍛婅閰嶇疆绠$悊"鑳芥甯歌闂〉闈�
+- [ ] 鍚勫姛鑳芥寜閽牴鎹潈闄愭纭樉绀�/闅愯棌
+
+## 鈿狅笍 甯歌闂
+
+### 1. 鑿滃崟涓嶆樉绀�
+
+**鍘熷洜**锛�
+- 鑿滃崟鏈垱寤烘垚鍔�
+- 鐢ㄦ埛瑙掕壊鏈垎閰嶈彍鍗曟潈闄�
+- 缂撳瓨鏈埛鏂�
+
+**瑙e喅鏂规硶**锛�
+```sql
+-- 妫�鏌ヨ彍鍗曟槸鍚﹀瓨鍦�
+SELECT * FROM sys_menu WHERE menu_name = '杞﹁締鐩戞帶';
+
+-- 妫�鏌ヨ鑹叉潈闄�
+SELECT rm.*, m.menu_name, m.perms 
+FROM sys_role_menu rm
+JOIN sys_menu m ON rm.menu_id = m.menu_id
+WHERE rm.role_id = YOUR_ROLE_ID 
+AND m.menu_name LIKE '%鍛婅%';
+
+-- 娓呴櫎鐢ㄦ埛缂撳瓨
+DELETE FROM sys_user_online WHERE user_name = 'YOUR_USERNAME';
+```
+
+### 2. 鎸夐挳涓嶆樉绀�
+
+**鍘熷洜**锛�
+- 鎸夐挳鏉冮檺鏈垎閰�
+- 鍓嶇v-hasPermi鎸囦护鏉冮檺鏍囪瘑涓嶅尮閰�
+
+**瑙e喅鏂规硶**锛�
+- 妫�鏌ヨ鑹叉槸鍚﹀垎閰嶄簡瀵瑰簲鐨勬寜閽潈闄�
+- 纭鍓嶇浠g爜涓殑鏉冮檺鏍囪瘑涓庢暟鎹簱涓�鑷�
+
+### 3. 閲嶅鎵цSQL鎶ラ敊
+
+**鍘熷洜**锛氳彍鍗曞凡瀛樺湪
+
+**瑙e喅鏂规硶**锛�
+- SQL鑴氭湰宸蹭娇鐢╜NOT EXISTS`闃查噸澶嶏紝姝e父鎯呭喌涓嶄細鎶ラ敊
+- 濡傛灉鎶ュ敮涓�閿啿绐侊紝璇存槑鏁版嵁宸插瓨鍦紝鍙互蹇界暐
+
+### 4. 鐖惰彍鍗旾D鑾峰彇澶辫触
+
+**鍘熷洜**锛氱埗鑿滃崟涓嶅瓨鍦ㄦ垨鍚嶇О涓嶅尮閰�
+
+**瑙e喅鏂规硶**锛�
+```sql
+-- 妫�鏌ョ埗鑿滃崟
+SELECT menu_id, menu_name FROM sys_menu WHERE menu_name = '杞﹁締鐩戞帶';
+
+-- 濡傛灉涓嶅瓨鍦紝鍏堟墽琛岀埗鑿滃崟鍒涘缓SQL
+```
+
+## 馃摓 鎶�鏈敮鎸�
+
+濡傛湁闂锛岃鍙傝�冿細
+- 馃搫 [瀹屾暣瀹炵幇鎬荤粨](./杞﹁締寮傚父杩愯鐩戞帶鍛婅-瀹屾暣瀹炵幇鎬荤粨.md)
+- 馃搫 [蹇�熼儴缃叉寚鍗梋(./杞﹁締寮傚父杩愯鐩戞帶鍛婅-蹇�熼儴缃叉寚鍗�.md)
+- 馃搫 [鍓嶇閮ㄧ讲鎸囧崡](./杞﹁締寮傚父杩愯鐩戞帶鍛婅-鍓嶇閮ㄧ讲鎸囧崡.md)
+
+---
+
+**鏂囨。鐗堟湰**: v1.0  
+**鏇存柊鏃堕棿**: 2026-01-12  
+**缁存姢浜哄憳**: AI寮�鍙戝姪鎵�
diff --git "a/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246\345\212\237\350\203\275\350\257\264\346\230\216.md" "b/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246\345\212\237\350\203\275\350\257\264\346\230\216.md"
new file mode 100644
index 0000000..c7456fc
--- /dev/null
+++ "b/doc/\350\275\246\350\276\206\345\274\202\345\270\270\350\277\220\350\241\214\347\233\221\346\216\247\345\221\212\350\255\246\345\212\237\350\203\275\350\257\264\346\230\216.md"
@@ -0,0 +1,287 @@
+# 杞﹁締寮傚父杩愯鐩戞帶鍛婅鍔熻兘瀹炵幇璇存槑
+
+## 鍔熻兘姒傝堪
+
+瀹炵幇杞﹁締鏃犱换鍔¤繍琛岃秴鍏噷鏁板憡璀﹀姛鑳斤紝褰撶洃鎺у埌杞﹁締鍦ㄦ棤缁戝畾浠诲姟鐘舵�佷笅杩愯瓒呰繃閰嶇疆鐨勫叕閲屾暟锛堥粯璁�10鍏噷锛夋椂锛岃嚜鍔ㄤ骇鐢熷憡璀﹀苟閫氳繃灏忕▼搴忓彂閫侀�氱煡缁欑浉鍏宠礋璐d汉銆�
+
+## 鍔熻兘鐗规��
+
+### 1. 鏅鸿兘鐩戞帶
+- **鏃堕棿绐楀彛鐩戞帶**锛氭瘡5鍒嗛挓鎵ц涓�娆★紝鐩戞帶鏈�杩�10鍒嗛挓锛堝彲閰嶇疆锛夊唴鐨勮溅杈嗚繍琛屾儏鍐�
+- **浠诲姟鐘舵�佹娴�**锛氳嚜鍔ㄨ瘑鍒溅杈嗘槸鍚︽湁姝e湪鎵ц鐨勪换鍔�
+- **閲岀▼鑷姩璁$畻**锛氬熀浜嶨PS鍒嗘閲岀▼璁板綍鑷姩绱杩愯鍏噷鏁�
+- **閮ㄩ棬鏅鸿兘璇嗗埆**锛氳嚜鍔ㄥ叧鑱旇溅杈嗗綊灞為儴闂ㄤ俊鎭�
+
+### 2. 鐏垫椿閰嶇疆
+- **鍏噷鏁伴槇鍊�**锛氶粯璁�10鍏噷锛屽彲閫氳繃绯荤粺閰嶇疆璋冩暣
+- **鍛婅棰戠巼闄愬埗**锛�
+  - 姣忓ぉ姣忚溅鏈�澶氬憡璀�5娆★紙鍙厤缃級
+  - 涓ゆ鍛婅鏈�灏忛棿闅�5鍒嗛挓锛堝彲閰嶇疆锛�
+- **鐩戞帶鏃堕棿绐楀彛**锛氶粯璁�10鍒嗛挓锛屽彲閰嶇疆
+- **閫氱煡鐢ㄦ埛閰嶇疆**锛氭敮鎸侀厤缃浐瀹氱敤鎴峰垪琛ㄦ垨鏍规嵁閮ㄩ棬鑷姩閫氱煡璐熻矗浜�
+
+### 3. 澶氭笭閬撻�氱煡
+- **浼佷笟寰俊閫氱煡**锛氶泦鎴愪紒涓氬井淇℃秷鎭帹閫�
+- **灏忕▼搴忛�氱煡**锛氶�氳繃灏忕▼搴忓彂閫佸憡璀︿俊鎭�
+- **閫氱煡鍐呭**锛氬寘鍚溅鐗屽彿銆佽繍琛屽叕閲屾暟銆佹椂闂存绛夊叧閿俊鎭�
+
+### 4. 瀹屾暣鐨勭鐞嗙晫闈�
+- **鍛婅鍒楄〃鏌ョ湅**锛氭敮鎸佹寜杞︾墝鍙枫�佹椂闂淬�侀儴闂ㄣ�佺姸鎬佺瓑澶氱淮搴︽煡璇�
+- **鍛婅璇︽儏鏌ョ湅**锛氭煡鐪嬪憡璀︾殑瀹屾暣淇℃伅
+- **鍛婅澶勭悊**锛氭敮鎸佸崟涓垨鎵归噺澶勭悊鍛婅锛岃褰曞鐞嗕汉鍜屽鐞嗘剰瑙�
+- **鏁版嵁瀵煎嚭**锛氭敮鎸佸鍑篍xcel杩涜鏁版嵁鍒嗘瀽
+- **缁熻鍔熻兘**锛氬睍绀烘湭澶勭悊鍛婅鏁伴噺銆佷粖鏃ュ憡璀︽暟閲忕瓑缁熻淇℃伅
+
+## 鎶�鏈疄鐜�
+
+### 鏁版嵁搴撹璁�
+
+#### 1. 杞﹁締寮傚父鍛婅璁板綍琛� (tb_vehicle_abnormal_alert)
+瀛樺偍姣忔鍛婅鐨勮缁嗕俊鎭細
+- 鍩烘湰淇℃伅锛氳溅杈咺D銆佽溅鐗屽彿銆佸憡璀︽椂闂淬�佺疮璁″叕閲屾暟
+- 鍛婅璇︽儏锛氬憡璀︾被鍨嬨�佸師鍥犳弿杩般�佸紑濮�/缁撴潫鏃堕棿
+- 澶勭悊淇℃伅锛氬鐞嗙姸鎬併�佸鐞嗕汉銆佸鐞嗘椂闂淬�佸鐞嗗娉�
+- 閫氱煡淇℃伅锛氶�氱煡鐘舵�併�侀�氱煡鏃堕棿銆侀�氱煡鐢ㄦ埛鍒楄〃
+- 閮ㄩ棬淇℃伅锛氬綊灞為儴闂↖D銆侀儴闂ㄥ悕绉�
+
+#### 2. 杞﹁締寮傚父鍛婅閰嶇疆琛� (tb_vehicle_alert_config)
+鏀寔鍏ㄥ眬銆侀儴闂ㄣ�佽溅杈嗕笁绾ч厤缃細
+- 閰嶇疆灞傜骇锛欸LOBAL(鍏ㄥ眬)/DEPT(閮ㄩ棬)/VEHICLE(杞﹁締)
+- 鍛婅鍙傛暟锛氬叕閲屾暟闃堝�笺�佹瘡鏃ュ憡璀︽鏁般�佸憡璀﹂棿闅�
+- 閫氱煡閰嶇疆锛氶�氱煡鐢ㄦ埛ID鍒楄〃銆侀�氱煡瑙掕壊鍒楄〃
+- 鍚敤鐘舵�侊細鍙伒娲诲紑鍚垨鍏抽棴鏌愪釜閰嶇疆
+
+### 鏍稿績缁勪欢
+
+#### 1. 瀹氭椂鐩戞帶浠诲姟 (VehicleAbnormalAlertTask)
+```java
+@Component("vehicleAbnormalAlertTask")
+public class VehicleAbnormalAlertTask {
+    // 姣�5鍒嗛挓鎵ц涓�娆�
+    public void monitorVehicleAbnormalRunning()
+}
+```
+
+**鐩戞帶娴佺▼**锛�
+1. 妫�鏌ュ姛鑳藉紑鍏虫槸鍚﹀惎鐢�
+2. 鍔犺浇閰嶇疆鍙傛暟锛堥槇鍊笺�侀鐜囬檺鍒剁瓑锛�
+3. 鏌ヨ鎵�鏈夋椿璺冭溅杈�
+4. 閫愯溅妫�鏌ワ細
+   - 鏄惁鏈夋鍦ㄦ墽琛岀殑浠诲姟
+   - 璁$畻鐩戞帶绐楀彛鍐呯殑杩愯閲岀▼
+   - 鍒ゆ柇鏄惁瓒呰繃闃堝��
+   - 妫�鏌ュ憡璀﹂鐜囬檺鍒�
+5. 鍒涘缓鍛婅璁板綍
+6. 鍙戦�侀�氱煡
+
+#### 2. 鍛婅鏈嶅姟 (VehicleAbnormalAlertService)
+鎻愪緵鍛婅鐨勫鍒犳敼鏌ャ�佸鐞嗙瓑鏍稿績涓氬姟閫昏緫锛�
+- `checkAndCreateAlert()`: 妫�鏌ュ苟鍒涘缓鍛婅
+- `handleAlert()`: 澶勭悊鍗曚釜鍛婅
+- `batchHandleAlert()`: 鎵归噺澶勭悊鍛婅
+
+#### 3. 鍛婅鎺у埗鍣� (VehicleAbnormalAlertController)
+鎻愪緵RESTful API鎺ュ彛锛�
+- `GET /system/vehicleAlert/list`: 鏌ヨ鍛婅鍒楄〃
+- `GET /system/vehicleAlert/{id}`: 鏌ヨ鍛婅璇︽儏
+- `PUT /system/vehicleAlert/handle/{id}`: 澶勭悊鍛婅
+- `GET /system/vehicleAlert/unhandledCount`: 鑾峰彇鏈鐞嗗憡璀︽暟
+- `POST /system/vehicleAlert/export`: 瀵煎嚭鍛婅鏁版嵁
+
+## 閰嶇疆璇存槑
+
+### 绯荤粺閰嶇疆鍙傛暟 (sys_config琛�)
+
+| 閰嶇疆閿� | 璇存槑 | 榛樿鍊� |
+|-------|------|--------|
+| vehicle.alert.enabled | 鍛婅鍔熻兘鎬诲紑鍏� | true |
+| vehicle.alert.mileage.threshold | 鍏噷鏁板憡璀﹂槇鍊硷紙鍏噷锛� | 10 |
+| vehicle.alert.daily.limit | 姣忔棩鏈�澶у憡璀︽鏁� | 5 |
+| vehicle.alert.interval.minutes | 鍛婅闂撮殧鏃堕棿锛堝垎閽燂級 | 5 |
+| vehicle.alert.time.window | 鐩戞帶鏃堕棿绐楀彛锛堝垎閽燂級 | 10 |
+| vehicle.alert.notify.users | 閫氱煡鐢ㄦ埛ID鍒楄〃 | 锛堢┖锛� |
+
+### 瀹氭椂浠诲姟閰嶇疆
+
+- **浠诲姟鍚嶇О**锛氳溅杈嗗紓甯歌繍琛岀洃鎺т换鍔�
+- **璋冪敤鐩爣**锛歚vehicleAbnormalAlertTask.monitorVehicleAbnormalRunning()`
+- **鎵ц棰戠巼**锛歚0 */5 * * * ?`锛堟瘡5鍒嗛挓鎵ц涓�娆★級
+- **骞跺彂鎺у埗**锛氱姝㈠苟鍙戞墽琛�
+- **榛樿鐘舵��**锛氭殏鍋滐紙闇�鎵嬪姩鍚敤锛�
+
+## 閮ㄧ讲姝ラ
+
+### 1. 鎵цSQL鑴氭湰
+```sql
+source sql/vehicle_abnormal_alert.sql;
+```
+
+璇ヨ剼鏈細鑷姩鍒涘缓锛�
+- 鍛婅璁板綍琛�
+- 鍛婅閰嶇疆琛�
+- 绯荤粺閰嶇疆鍙傛暟
+- 瀹氭椂浠诲姟
+- 鑿滃崟鏉冮檺
+
+### 2. 閰嶇疆閫氱煡鐢ㄦ埛
+鍦ㄧ郴缁熼厤缃腑璁剧疆 `vehicle.alert.notify.users`锛屽~鍏ユ帴鏀跺憡璀︾殑鐢ㄦ埛ID鍒楄〃锛堥�楀彿鍒嗛殧锛�
+
+### 3. 鍚敤瀹氭椂浠诲姟
+鍦�"绯荤粺鐩戞帶 > 瀹氭椂浠诲姟"涓壘鍒�"杞﹁締寮傚父杩愯鐩戞帶浠诲姟"锛岀偣鍑�"鎭㈠"鎸夐挳鍚敤
+
+### 4. 閰嶇疆浼佷笟寰俊锛堝彲閫夛級
+濡傞渶浣跨敤浼佷笟寰俊閫氱煡锛岀‘淇濅互涓嬮厤缃纭細
+- `qy_wechat.enable = true`
+- `qy_wechat.corp_id` = 浼佷笟ID
+- `qy_wechat.corp_secret` = 搴旂敤Secret
+- `qy_wechat.agent_id` = 搴旂敤ID
+
+## 浣跨敤鎸囧崡
+
+### 绠$悊鍛樻搷浣�
+
+#### 1. 鏌ョ湅鍛婅鍒楄〃
+- 杩涘叆"杞﹁締绠$悊 > 杞﹁締寮傚父鍛婅"
+- 鍙寜杞︾墝鍙枫�佹椂闂磋寖鍥淬�侀儴闂ㄣ�佺姸鎬佺瓫閫�
+- 榛樿鎸夊憡璀︽椂闂村�掑簭鎺掑垪
+
+#### 2. 澶勭悊鍛婅
+- 鐐瑰嚮"澶勭悊"鎸夐挳
+- 濉啓澶勭悊澶囨敞
+- 鎻愪氦鍚庡憡璀︾姸鎬佸彉鏇翠负"宸插鐞�"
+
+#### 3. 鎵归噺澶勭悊
+- 鍕鹃�夊涓憡璀�
+- 鐐瑰嚮"鎵归噺澶勭悊"
+- 濉啓缁熶竴鐨勫鐞嗗娉�
+
+#### 4. 瀵煎嚭鏁版嵁
+- 璁剧疆绛涢�夋潯浠�
+- 鐐瑰嚮"瀵煎嚭"鎸夐挳
+- 涓嬭浇Excel鏂囦欢杩涜鍒嗘瀽
+
+#### 5. 璋冩暣閰嶇疆
+- 杩涘叆"绯荤粺绠$悊 > 鍙傛暟璁剧疆"
+- 鎼滅储"vehicle.alert"
+- 淇敼鐩稿叧鍙傛暟
+- 淇敼鍚庣珛鍗崇敓鏁堬紝鏃犻渶閲嶅惎
+
+### 鐩戞帶鍘熺悊
+
+#### 閲岀▼璁$畻
+- 鍩轰簬`tb_vehicle_gps_segment_mileage`琛�
+- 姣�5鍒嗛挓鑷姩鍒嗘缁熻GPS閲岀▼
+- 鏌ヨ鐩戞帶绐楀彛鍐呯殑鎵�鏈夊垎娈甸噷绋嬭褰�
+- 绱姞璁$畻鎬昏繍琛屽叕閲屾暟
+
+#### 浠诲姟鍏宠仈鍒ゆ柇
+- 鏌ヨ`sys_task`鍜宍sys_task_vehicle`琛�
+- 妫�鏌ヨ溅杈嗗湪鐩戞帶鏃堕棿绐楀彛鍐呮槸鍚︽湁浠诲姟
+- 鎺掗櫎宸插畬鎴愬拰宸插彇娑堢殑浠诲姟
+- 鍙湁瀹屽叏鏃犱换鍔℃椂鎵嶈Е鍙戝憡璀�
+
+#### 棰戠巼鎺у埗
+- **姣忔棩娆℃暟闄愬埗**锛氭煡璇㈠綋澶╄杞﹀凡鍛婅娆℃暟锛岃揪鍒颁笂闄愬垯璺宠繃
+- **鏃堕棿闂撮殧闄愬埗**锛氭煡璇㈡渶鍚庝竴娆″憡璀︽椂闂达紝鏈揪鍒伴棿闅斿垯璺宠繃
+- 闃叉棰戠箒鍛婅楠氭壈
+
+## 鍛婅鐘舵�佹祦杞�
+
+```
+鏈鐞�(0) -> 宸插鐞�(1)
+         \-> 宸插拷鐣�(2)
+```
+
+- **鏈鐞�**锛氭柊鍒涘缓鐨勫憡璀︼紝寰呭鐞�
+- **宸插鐞�**锛氱鐞嗗憳宸插鐞嗗苟濉啓浜嗗鐞嗘剰瑙�
+- **宸插拷鐣�**锛氱鐞嗗憳纭涓鸿鎶ユ垨鏃犻渶澶勭悊
+
+## 娉ㄦ剰浜嬮」
+
+1. **GPS鏁版嵁渚濊禆**
+   - 闇�瑕丟PS鍒嗘閲岀▼璁$畻浠诲姟姝e父杩愯
+   - 纭繚`tb_vehicle_gps_segment_mileage`琛ㄦ湁鏁版嵁
+   - 寤鸿鍏堣繍琛孏PS閲岀▼璁$畻浠诲姟娴嬭瘯
+
+2. **鎬ц兘浼樺寲**
+   - 鐩戞帶浠诲姟姣�5鍒嗛挓鎵ц涓�娆�
+   - 姣忔鍙煡璇㈡渶杩�10鍒嗛挓鐨勬暟鎹�
+   - 鏁版嵁搴撳凡寤虹珛蹇呰鐨勭储寮�
+   - 澶ч噺杞﹁締鏃舵敞鎰忕洃鎺ф墽琛屾椂闂�
+
+3. **閫氱煡閰嶇疆**
+   - 浼樺厛浣跨敤閰嶇疆鐨勭敤鎴峰垪琛�
+   - 鍏舵鏍规嵁杞﹁締褰掑睘閮ㄩ棬鏌ユ壘璐熻矗浜�
+   - 鏈�鍚庝娇鐢ㄥ叏灞�榛樿鐢ㄦ埛鍒楄〃
+   - 纭繚鑷冲皯閰嶇疆涓�绉嶉�氱煡鏂瑰紡
+
+4. **娴嬭瘯寤鸿**
+   - 鍏堝湪娴嬭瘯鐜楠岃瘉
+   - 璋冩暣闃堝�间负杈冨皬鍊硷紙濡�1鍏噷锛夋柟渚胯Е鍙�
+   - 瑙傚療鍛婅璁板綍鍜岄�氱煡鏄惁姝e父
+   - 纭棰戠巼闄愬埗鏄惁鐢熸晥
+
+5. **鐩戞帶璋冧紭**
+   - 鏍规嵁瀹為檯鎯呭喌璋冩暣鍏噷鏁伴槇鍊�
+   - 鍚堢悊璁剧疆姣忔棩鍛婅娆℃暟
+   - 閫傚綋璋冩暣鐩戞帶鏃堕棿绐楀彛
+   - 閬垮厤鍛婅杩囦簬棰戠箒鎴栭仐婕�
+
+## 鎵╁睍鍔熻兘
+
+### 鏈潵鍙墿灞曠殑鍔熻兘鏂瑰悜
+
+1. **澶氱鍛婅绫诲瀷**
+   - 闀挎椂闂村仠杞﹀憡璀�
+   - 瓒呴�熷憡璀�
+   - 鍋忕璺嚎鍛婅
+
+2. **鍛婅瑙勫垯寮曟搸**
+   - 鑷畾涔夊憡璀﹁鍒�
+   - 瑙勫垯缁勫悎涓庝紭鍏堢骇
+   - 鍔ㄦ�佽皟鏁磋鍒�
+
+3. **鏁版嵁鍒嗘瀽**
+   - 鍛婅瓒嬪娍鍒嗘瀽
+   - 杞﹁締寮傚父琛屼负缁熻
+   - 閮ㄩ棬鍛婅瀵规瘮
+
+4. **鏅鸿兘鎺ㄨ崘**
+   - 鍩轰簬鍘嗗彶鏁版嵁棰勬祴寮傚父
+   - 鎺ㄨ崘鏈�浼橀厤缃弬鏁�
+   - 鑷姩璇嗗埆璇姤
+
+## 鎶�鏈敮鎸�
+
+濡傛湁闂锛岃鑱旂郴鎶�鏈敮鎸佸洟闃熸垨鏌ョ湅鐩稿叧鏂囨。锛�
+- 绯荤粺鏃ュ織锛歚/logs/sys-*.log`
+- 瀹氭椂浠诲姟鏃ュ織锛歚/monitor/job/log`
+- GPS閲岀▼璁$畻璇存槑锛歚/doc/GPS鍒嗘閲岀▼璁$畻.md`
+
+## 鏂囦欢娓呭崟
+
+### 鍚庣浠g爜
+- `ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleAbnormalAlert.java` - 鍛婅瀹炰綋绫�
+- `ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleAlertConfig.java` - 閰嶇疆瀹炰綋绫�
+- `ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleAbnormalAlertMapper.java` - 鏁版嵁璁块棶鎺ュ彛
+- `ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleAbnormalAlertService.java` - 涓氬姟鎺ュ彛
+- `ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleAbnormalAlertServiceImpl.java` - 涓氬姟瀹炵幇
+- `ruoyi-system/src/main/resources/mapper/system/VehicleAbnormalAlertMapper.xml` - MyBatis鏄犲皠鏂囦欢
+- `ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleAbnormalAlertController.java` - 鎺у埗鍣�
+- `ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleAbnormalAlertTask.java` - 瀹氭椂浠诲姟
+
+### 鏁版嵁搴撹剼鏈�
+- `sql/vehicle_abnormal_alert.sql` - 寤鸿〃鍙婂垵濮嬪寲鑴氭湰
+
+### 鏂囨。
+- 鏈枃妗� - 瀹屾暣鐨勫姛鑳借鏄庡拰浣跨敤鎸囧崡
+
+## 鏇存柊鏃ュ織
+
+### V1.0.0 (2026-01-12)
+- 鉁� 瀹炵幇鍩虹鐩戞帶鍔熻兘
+- 鉁� 鏀寔浼佷笟寰俊閫氱煡
+- 鉁� 瀹屾暣鐨勭鐞嗙晫闈�
+- 鉁� 鐏垫椿鐨勯厤缃郴缁�
+- 鉁� 鍛婅棰戠巼鎺у埗
+- 鉁� 鏁版嵁瀵煎嚭鍔熻兘
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 d4f92b1..6b3cba6 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
@@ -2,16 +2,27 @@
 
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.utils.HospitalTokenizerUtil;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.system.domain.HospData;
+import com.ruoyi.system.domain.HospitalTokenizerTask;
+import com.ruoyi.system.domain.TbHospData;
 import com.ruoyi.system.mapper.HospDataMapper;
+import com.ruoyi.system.service.HospitalTokenizerAsyncService;
 import com.ruoyi.system.service.ISQLHospDataService;
+import com.ruoyi.system.service.ITbHospDataService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+import java.util.UUID;
 
 /**
  * 鍖婚櫌鏁版嵁Controller
@@ -30,6 +41,15 @@
 
     @Autowired
     private ISQLHospDataService sqlHospDataService;
+    
+    @Autowired
+    private ITbHospDataService tbHospDataService;
+    
+    @Autowired
+    private com.ruoyi.system.mapper.TbHospDataMapper tbHospDataMapper;
+    
+    @Autowired
+    private HospitalTokenizerAsyncService asyncService;
     
     /**
      * 鎼滅储鍖婚櫌锛堜粠MySQL tb_hosp_data琛ㄦ煡璇級
@@ -181,4 +201,255 @@
         
         return success(hospitals);
     }
+    
+    /**
+     * 鎵归噺鐢熸垚鎵�鏈夊尰闄㈢殑鍒嗚瘝锛堝紓姝ワ級
+     * 绠$悊鍛樻帴鍙o紝鐢ㄤ簬鍒濆鍖栨垨閲嶆柊鐢熸垚鍖婚櫌鍒嗚瘝
+     * 
+     * @return 浠诲姟ID
+     */
+    @GetMapping("/generateKeywords")
+    public AjaxResult generateAllHospitalKeywords() {
+        logger.info("寮�濮嬫壒閲忕敓鎴愬尰闄㈠垎璇嶏紙寮傛锛�...");
+        
+        try {
+            // 鐢熸垚浠诲姟ID
+            String taskId = UUID.randomUUID().toString().replace("-", "");
+            
+            // 寮傛鎵ц浠诲姟
+            asyncService.executeTokenizerTask(taskId);
+            
+            logger.info("鍖婚櫌鍒嗚瘝浠诲姟宸插惎鍔�: taskId={}", taskId);
+            
+            // 绔嬪嵆杩斿洖浠诲姟ID
+            return success()
+                .put("taskId", taskId)
+                .put("message", "鍒嗚瘝浠诲姟宸插惎鍔紝璇锋煡璇换鍔¤繘搴�");
+            
+        } catch (Exception e) {
+            logger.error("鍚姩鍖婚櫌鍒嗚瘝浠诲姟澶辫触", e);
+            return error("鍚姩澶辫触锛�" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 鏌ヨ鍖婚櫌鍒嗚瘝浠诲姟杩涘害
+     * 
+     * @param taskId 浠诲姟ID
+     * @return 浠诲姟杩涘害淇℃伅
+     */
+    @GetMapping("/getTaskProgress")
+    public AjaxResult getTaskProgress(@RequestParam("taskId") String taskId) {
+        try {
+            HospitalTokenizerTask task = asyncService.getTaskStatus(taskId);
+            
+            if (task == null) {
+                return error("浠诲姟涓嶅瓨鍦ㄦ垨宸茶繃鏈�");
+            }
+            
+            return success(task);
+            
+        } catch (Exception e) {
+            logger.error("鏌ヨ浠诲姟杩涘害澶辫触: taskId={}", taskId, e);
+            return error("鏌ヨ澶辫触锛�" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 鍩轰簬鍒嗚瘝鍖归厤鎼滅储鍖婚櫌
+     * 鍓嶇浼犲叆鍖婚櫌淇℃伅锛岃繘琛屽垎璇嶅悗涓庢暟鎹簱涓殑鍒嗚瘝鍖归厤
+     * 鏍规嵁鍖归厤鐨勫垎璇嶆暟閲忚繘琛屾潈閲嶆帓搴忥紝鍖归厤瓒婂鎺掑悕瓒婇潬鍓�
+     * 
+     * @param searchText 鎼滅储鏂囨湰锛堝尰闄㈠悕绉般�佸湴鍧�绛夛級
+     * @param pageSize 杩斿洖缁撴灉鏁伴噺闄愬埗锛堥粯璁�50锛�
+     * @return 鍖归厤鐨勫尰闄㈠垪琛紙鎸夊尮閰嶅害鎺掑簭锛�
+     */
+    @GetMapping("/searchByKeywords")
+    public AjaxResult searchHospitalsByKeywords(
+            @RequestParam("searchText") String searchText,
+            @RequestParam(value = "pageSize", required = false, defaultValue = "50") Integer pageSize) {
+        
+        logger.info("鍩轰簬鍒嗚瘝鍖归厤鎼滅储鍖婚櫌锛歴earchText={}, pageSize={}", searchText, pageSize);
+        
+        if (searchText == null || searchText.trim().isEmpty()) {
+            return error("鎼滅储鏂囨湰涓嶈兘涓虹┖");
+        }
+        
+        try {
+            long startTime = System.currentTimeMillis();
+            
+            // 1. 瀵瑰墠绔紶鍏ョ殑鎼滅储鏂囨湰杩涜鍒嗚瘝
+            String searchKeywords = HospitalTokenizerUtil.tokenizeSearchText(searchText);
+            logger.info("鎼滅储鏂囨湰鍒嗚瘝缁撴灉锛歿}", searchKeywords);
+            
+            if (searchKeywords.isEmpty()) {
+                return success(new ArrayList<>());
+            }
+            
+            // 2. 灏嗗垎璇嶇粨鏋滄媶鍒嗕负鍏抽敭璇嶅垪琛紝鐢ㄤ簬鏁版嵁搴撻杩囨护
+            String[] keywordArray = searchKeywords.split(",");
+            List<String> keywordList = new ArrayList<>();
+            for (String keyword : keywordArray) {
+                String trimmed = keyword.trim();
+                if (!trimmed.isEmpty() && trimmed.length() >= 2) { // 鍙娇鐢�2涓瓧浠ヤ笂鐨勫叧閿瘝
+                    keywordList.add(trimmed);
+                }
+            }
+            
+            if (keywordList.isEmpty()) {
+                logger.warn("娌℃湁鏈夋晥鐨勫叧閿瘝鐢ㄤ簬鎼滅储");
+                return success(new ArrayList<>());
+            }
+            
+            logger.info("浣跨敤鍏抽敭璇嶈繘琛屾暟鎹簱棰勮繃婊�: {}", keywordList);
+            
+            // 3. 閫氳繃鏁版嵁搴撳眰闈㈤杩囨护锛屽彧鏌ヨ鍙兘鍖归厤鐨勫尰闄紙鑰屼笉鏄墍鏈夊尰闄級
+            List<TbHospData> candidateHospitals = tbHospDataMapper.selectTbHospDataByKeywords(keywordList, "0");
+            
+            long queryTime = System.currentTimeMillis();
+            logger.info("鏁版嵁搴撻杩囨护瀹屾垚锛屽�欓�夊尰闄㈡暟閲�: {}, 鑰楁椂: {}ms", candidateHospitals.size(), queryTime - startTime);
+                        
+            // 4. 鎻愬彇鍊欓�夊尰闄㈢殑鍦板尯鍚嶇О锛堜粠 hopsArea 瀛楁锛�
+            Set<String> districtNames = new HashSet<>();
+            for (TbHospData hospital : candidateHospitals) {
+                if (StringUtils.isNotBlank(hospital.getHopsArea())) {
+                    // 鎻愬彇鍦板尯鍚嶏紝绉婚櫎甯歌鍚庣紑
+                    String area = hospital.getHopsArea()
+                        .replace("鍖�", "")
+                        .replace("甯�", "")
+                        .replace("鍘�", "")
+                        .trim();
+                    if (area.length() > 0) {
+                        districtNames.add(area);
+                    }
+                }
+            }
+                        
+            logger.info("鎻愬彇鍒� {} 涓嫭鐗瑰湴鍖哄悕绉�", districtNames.size());
+                        
+            // 5. 瀵瑰�欓�夊尰闄㈣绠楀尮閰嶅垎鏁帮紝骞惰繃婊ゅ嚭鏈夊尮閰嶇殑鍖婚櫌
+            List<HospitalMatchResult> matchResults = new ArrayList<>();
+                        
+            long matchStartTime = System.currentTimeMillis();
+                        
+            for (TbHospData hospital : candidateHospitals) {
+                if (hospital.getHospKeywords() == null || hospital.getHospKeywords().isEmpty()) {
+                    continue;
+                }
+                            
+                // 璁$畻鍖归厤鍒嗘暟锛堜紶鍏ュ尰闄㈠悕绉板拰鍦板尯鍚嶇О闆嗗悎锛�
+                int matchScore = HospitalTokenizerUtil.calculateMatchScore(
+                    searchKeywords, 
+                    hospital.getHospKeywords(),
+                    hospital.getHospName(),
+                    districtNames
+                );
+                            
+                // 鍙繚鐣欐湁鍖归厤鐨勫尰闄�
+                if (matchScore > 0) {
+                    matchResults.add(new HospitalMatchResult(hospital, matchScore));
+                }
+            }
+                        
+            long matchTime = System.currentTimeMillis();
+            logger.info("鍖归厤璁$畻瀹屾垚锛屾壘鍒� {} 涓尮閰嶇殑鍖婚櫌锛岃�楁椂: {}ms", matchResults.size(), matchTime - matchStartTime);
+            
+            // 4. 鎸夊尮閰嶅垎鏁伴檷搴忔帓搴忥紙鍒嗘暟瓒婇珮鎺掑悕瓒婇潬鍓嶏級
+            matchResults.sort(Comparator.comparingInt(HospitalMatchResult::getMatchScore).reversed());
+            
+            // 5. 闄愬埗杩斿洖鏁伴噺
+            if (pageSize != null && pageSize > 0 && matchResults.size() > pageSize) {
+                matchResults = matchResults.subList(0, pageSize);
+            }
+            
+            // 6. 杞崲涓篐ospData瀵硅薄杩斿洖锛堝寘鍚尮閰嶅垎鏁帮級
+            List<HospDataWithScore> result = new ArrayList<>();
+            for (HospitalMatchResult matchResult : matchResults) {
+                TbHospData tbHospData = matchResult.getHospital();
+                HospData hospData = convertToHospData(tbHospData);
+                result.add(new HospDataWithScore(hospData, matchResult.getMatchScore()));
+                logger.debug("鍖婚櫌: {}, 鍖归厤鍒嗘暟: {}", 
+                    hospData.getHospName(), matchResult.getMatchScore());
+            }
+            
+            logger.info("杩斿洖 {} 涓尰闄㈢粨鏋�", result.size());
+            
+            long totalTime = System.currentTimeMillis() - startTime;
+            logger.info("鎼滅储瀹屾垚 - 鎬昏�楁椂: {}ms, 鏁版嵁搴撴煡璇�: {}ms, 鍖归厤璁$畻: {}ms", 
+                totalTime, queryTime - startTime, matchTime - matchStartTime);
+            
+            return success(result);
+            
+        } catch (Exception e) {
+            logger.error("鍒嗚瘝鍖归厤鎼滅储澶辫触", e);
+            return error("鎼滅储澶辫触锛�" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 灏員bHospData杞崲涓篐ospData
+     */
+    private HospData convertToHospData(TbHospData tbHospData) {
+        HospData hospData = new HospData();
+        hospData.setHospId(tbHospData.getLegacyHospId());
+        hospData.setHospName(tbHospData.getHospName());
+        hospData.setHospCityId(tbHospData.getHospCityId());
+        hospData.setHospShort(tbHospData.getHospShort());
+        hospData.setHopsProvince(tbHospData.getHopsProvince());
+        hospData.setHopsCity(tbHospData.getHopsCity());
+        hospData.setHopsArea(tbHospData.getHopsArea());
+        hospData.setHospAddress(tbHospData.getHospAddress());
+        hospData.setHospTel(tbHospData.getHospTel());
+        hospData.setHospUnitId(tbHospData.getHospUnitId());
+        hospData.setHospState(tbHospData.getHospState());
+        hospData.setHospOaId(tbHospData.getHospOaId());
+        hospData.setHospIntroducerId(tbHospData.getHospIntroducerId());
+        if (tbHospData.getHospIntroducerDate() != null) {
+            hospData.setHospIntroducerDate(tbHospData.getHospIntroducerDate().toString());
+        }
+        hospData.setHospLevel(tbHospData.getHospLevel());
+        return hospData;
+    }
+    
+    /**
+     * 鍖婚櫌鍖归厤缁撴灉鍐呴儴绫�
+     */
+    private static class HospitalMatchResult {
+        private TbHospData hospital;
+        private int matchScore;
+        
+        public HospitalMatchResult(TbHospData hospital, int matchScore) {
+            this.hospital = hospital;
+            this.matchScore = matchScore;
+        }
+        
+        public TbHospData getHospital() {
+            return hospital;
+        }
+        
+        public int getMatchScore() {
+            return matchScore;
+        }
+    }
+    
+    /**
+     * 鍖婚櫌鏁版嵁涓庡尮閰嶅垎鏁板寘瑁呯被
+     */
+    private static class HospDataWithScore {
+        private HospData hospital;
+        private int matchScore;
+        
+        public HospDataWithScore(HospData hospital, int matchScore) {
+            this.hospital = hospital;
+            this.matchScore = matchScore;
+        }
+        
+        public HospData getHospital() {
+            return hospital;
+        }
+        
+        public int getMatchScore() {
+            return matchScore;
+        }
+    }
 }
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleAbnormalAlertController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleAbnormalAlertController.java
new file mode 100644
index 0000000..bc3769f
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleAbnormalAlertController.java
@@ -0,0 +1,149 @@
+package com.ruoyi.web.controller.system;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.system.domain.VehicleAbnormalAlert;
+import com.ruoyi.system.service.IVehicleAbnormalAlertService;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.page.TableDataInfo;
+
+/**
+ * 杞﹁締寮傚父鍛婅Controller
+ * 
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/system/vehicleAlert")
+public class VehicleAbnormalAlertController extends BaseController {
+    
+    @Autowired
+    private IVehicleAbnormalAlertService vehicleAbnormalAlertService;
+
+    /**
+     * 鏌ヨ杞﹁締寮傚父鍛婅鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('system:vehicleAlert:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(VehicleAbnormalAlert vehicleAbnormalAlert) {
+        startPage();
+        List<VehicleAbnormalAlert> list = vehicleAbnormalAlertService.selectVehicleAbnormalAlertList(vehicleAbnormalAlert);
+        return getDataTable(list);
+    }
+
+    /**
+     * 瀵煎嚭杞﹁締寮傚父鍛婅鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('system:vehicleAlert:export')")
+    @Log(title = "杞﹁締寮傚父鍛婅", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, VehicleAbnormalAlert vehicleAbnormalAlert) {
+        List<VehicleAbnormalAlert> list = vehicleAbnormalAlertService.selectVehicleAbnormalAlertList(vehicleAbnormalAlert);
+        ExcelUtil<VehicleAbnormalAlert> util = new ExcelUtil<VehicleAbnormalAlert>(VehicleAbnormalAlert.class);
+        util.exportExcel(response, list, "杞﹁締寮傚父鍛婅鏁版嵁");
+    }
+
+    /**
+     * 鑾峰彇杞﹁締寮傚父鍛婅璇︾粏淇℃伅
+     */
+    @PreAuthorize("@ss.hasPermi('system:vehicleAlert:query')")
+    @GetMapping(value = "/{alertId}")
+    public AjaxResult getInfo(@PathVariable("alertId") Long alertId) {
+        return success(vehicleAbnormalAlertService.selectVehicleAbnormalAlertByAlertId(alertId));
+    }
+
+    /**
+     * 鏂板杞﹁締寮傚父鍛婅
+     */
+    @PreAuthorize("@ss.hasPermi('system:vehicleAlert:add')")
+    @Log(title = "杞﹁締寮傚父鍛婅", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody VehicleAbnormalAlert vehicleAbnormalAlert) {
+        return toAjax(vehicleAbnormalAlertService.insertVehicleAbnormalAlert(vehicleAbnormalAlert));
+    }
+
+    /**
+     * 淇敼杞﹁締寮傚父鍛婅
+     */
+    @PreAuthorize("@ss.hasPermi('system:vehicleAlert:edit')")
+    @Log(title = "杞﹁締寮傚父鍛婅", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody VehicleAbnormalAlert vehicleAbnormalAlert) {
+        return toAjax(vehicleAbnormalAlertService.updateVehicleAbnormalAlert(vehicleAbnormalAlert));
+    }
+
+    /**
+     * 鍒犻櫎杞﹁締寮傚父鍛婅
+     */
+    @PreAuthorize("@ss.hasPermi('system:vehicleAlert:remove')")
+    @Log(title = "杞﹁締寮傚父鍛婅", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{alertIds}")
+    public AjaxResult remove(@PathVariable Long[] alertIds) {
+        return toAjax(vehicleAbnormalAlertService.deleteVehicleAbnormalAlertByAlertIds(alertIds));
+    }
+
+    /**
+     * 澶勭悊鍛婅
+     */
+    @PreAuthorize("@ss.hasPermi('system:vehicleAlert:handle')")
+    @Log(title = "澶勭悊杞﹁締鍛婅", businessType = BusinessType.UPDATE)
+    @PutMapping("/handle/{alertId}")
+    public AjaxResult handleAlert(@PathVariable Long alertId, @RequestBody VehicleAbnormalAlert vehicleAbnormalAlert) {
+        Long handlerId = getUserId();
+        String handlerName = getUsername();
+        String handleRemark = vehicleAbnormalAlert.getHandleRemark();
+        
+        return toAjax(vehicleAbnormalAlertService.handleAlert(alertId, handlerId, handlerName, handleRemark));
+    }
+
+    /**
+     * 鎵归噺澶勭悊鍛婅
+     */
+    @PreAuthorize("@ss.hasPermi('system:vehicleAlert:handle')")
+    @Log(title = "鎵归噺澶勭悊杞﹁締鍛婅", businessType = BusinessType.UPDATE)
+    @PutMapping("/batchHandle")
+    public AjaxResult batchHandleAlert(@RequestBody VehicleAbnormalAlert vehicleAbnormalAlert) {
+        Long[] alertIds = vehicleAbnormalAlert.getAlertId() != null ? 
+                new Long[]{vehicleAbnormalAlert.getAlertId()} : new Long[0];
+        Long handlerId = getUserId();
+        String handlerName = getUsername();
+        String handleRemark = vehicleAbnormalAlert.getHandleRemark();
+        
+        return toAjax(vehicleAbnormalAlertService.batchHandleAlert(alertIds, handlerId, handlerName, handleRemark));
+    }
+
+    /**
+     * 鑾峰彇鏈鐞嗗憡璀︽暟閲�
+     */
+    @GetMapping("/unhandledCount")
+    public AjaxResult getUnhandledCount() {
+        VehicleAbnormalAlert query = new VehicleAbnormalAlert();
+        query.setStatus("0"); // 鏈鐞�
+        List<VehicleAbnormalAlert> list = vehicleAbnormalAlertService.selectVehicleAbnormalAlertList(query);
+        return success(list.size());
+    }
+
+    /**
+     * 鑾峰彇浠婃棩鍛婅鏁伴噺
+     */
+    @GetMapping("/todayCount")
+    public AjaxResult getTodayCount() {
+        VehicleAbnormalAlert query = new VehicleAbnormalAlert();
+        query.setAlertDate(new java.util.Date());
+        List<VehicleAbnormalAlert> list = vehicleAbnormalAlertService.selectVehicleAbnormalAlertList(query);
+        return success(list.size());
+    }
+}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleAlertConfigController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleAlertConfigController.java
new file mode 100644
index 0000000..471c8da
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleAlertConfigController.java
@@ -0,0 +1,106 @@
+package com.ruoyi.web.controller.system;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.system.domain.VehicleAlertConfig;
+import com.ruoyi.system.service.IVehicleAlertConfigService;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.page.TableDataInfo;
+
+/**
+ * 杞﹁締鍛婅閰嶇疆Controller
+ * 
+ * @author ruoyi
+ * @date 2026-01-12
+ */
+@RestController
+@RequestMapping("/system/vehicleAlertConfig")
+public class VehicleAlertConfigController extends BaseController
+{
+    @Autowired
+    private IVehicleAlertConfigService vehicleAlertConfigService;
+
+    /**
+     * 鏌ヨ杞﹁締鍛婅閰嶇疆鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('system:vehicleAlertConfig:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(VehicleAlertConfig vehicleAlertConfig)
+    {
+        startPage();
+        List<VehicleAlertConfig> list = vehicleAlertConfigService.selectVehicleAlertConfigList(vehicleAlertConfig);
+        return getDataTable(list);
+    }
+
+    /**
+     * 瀵煎嚭杞﹁締鍛婅閰嶇疆鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('system:vehicleAlertConfig:export')")
+    @Log(title = "杞﹁締鍛婅閰嶇疆", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, VehicleAlertConfig vehicleAlertConfig)
+    {
+        List<VehicleAlertConfig> list = vehicleAlertConfigService.selectVehicleAlertConfigList(vehicleAlertConfig);
+        ExcelUtil<VehicleAlertConfig> util = new ExcelUtil<VehicleAlertConfig>(VehicleAlertConfig.class);
+        util.exportExcel(response, list, "杞﹁締鍛婅閰嶇疆鏁版嵁");
+    }
+
+    /**
+     * 鑾峰彇杞﹁締鍛婅閰嶇疆璇︾粏淇℃伅
+     */
+    @PreAuthorize("@ss.hasPermi('system:vehicleAlertConfig:query')")
+    @GetMapping(value = "/{configId}")
+    public AjaxResult getInfo(@PathVariable("configId") Long configId)
+    {
+        return success(vehicleAlertConfigService.selectVehicleAlertConfigByConfigId(configId));
+    }
+
+    /**
+     * 鏂板杞﹁締鍛婅閰嶇疆
+     */
+    @PreAuthorize("@ss.hasPermi('system:vehicleAlertConfig:add')")
+    @Log(title = "杞﹁締鍛婅閰嶇疆", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody VehicleAlertConfig vehicleAlertConfig)
+    {
+        vehicleAlertConfig.setCreateBy(getUsername());
+        return toAjax(vehicleAlertConfigService.insertVehicleAlertConfig(vehicleAlertConfig));
+    }
+
+    /**
+     * 淇敼杞﹁締鍛婅閰嶇疆
+     */
+    @PreAuthorize("@ss.hasPermi('system:vehicleAlertConfig:edit')")
+    @Log(title = "杞﹁締鍛婅閰嶇疆", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody VehicleAlertConfig vehicleAlertConfig)
+    {
+        vehicleAlertConfig.setUpdateBy(getUsername());
+        return toAjax(vehicleAlertConfigService.updateVehicleAlertConfig(vehicleAlertConfig));
+    }
+
+    /**
+     * 鍒犻櫎杞﹁締鍛婅閰嶇疆
+     */
+    @PreAuthorize("@ss.hasPermi('system:vehicleAlertConfig:remove')")
+    @Log(title = "杞﹁締鍛婅閰嶇疆", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{configIds}")
+    public AjaxResult remove(@PathVariable Long[] configIds)
+    {
+        return toAjax(vehicleAlertConfigService.deleteVehicleAlertConfigByConfigIds(configIds));
+    }
+}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleSyncController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleSyncController.java
index fc61e22..6280257 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleSyncController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleSyncController.java
@@ -1,37 +1,165 @@
 package com.ruoyi.web.controller.system;
 
+import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.system.domain.VehicleInfo;
+import com.ruoyi.system.domain.VehicleSyncDTO;
+import com.ruoyi.system.domain.vo.VehicleSyncVO;
+import com.ruoyi.system.mapper.VehicleInfoMapper;
+import com.ruoyi.system.service.IVehicleInfoService;
 import com.ruoyi.system.service.IVehicleSyncDataService;
-import com.ruoyi.system.service.IVehicleSyncService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
- * 杞﹁締鍚屾Controller
- * 
+ * 杞﹁締鍚屾绠$悊Controller
+ *
  * @author ruoyi
- * @date 2025-10-20
  */
 @RestController
-@RequestMapping("/system/vehicle/sync")
-public class VehicleSyncController extends BaseController
-{
-    @Autowired
-    private IVehicleSyncService vehicleSyncService;
+@RequestMapping("/system/vehicleSync")
+public class VehicleSyncController extends BaseController {
+
+    private static final Logger log = LoggerFactory.getLogger(VehicleSyncController.class);
 
     @Autowired
     private IVehicleSyncDataService vehicleSyncDataService;
+
+    @Autowired
+    private VehicleInfoMapper vehicleInfoMapper;
+
+    @Autowired
+    private IVehicleInfoService vehicleInfoService;
+
     /**
-     * 鎵嬪姩鍚屾鏃х郴缁熻溅杈嗘暟鎹�
+     * 鏌ヨ杞﹁締鍚屾鍒楄〃锛堟樉绀烘棫绯荤粺杞﹁締鍙婂悓姝ョ姸鎬侊級
      */
-    @PreAuthorize("@ss.hasPermi('system:vehicle:sync')")
-    @PostMapping("/legacy")
-    public AjaxResult syncLegacyVehicles()
-    {
-        return vehicleSyncService.syncVehicles(this.vehicleSyncDataService.getVehiclesFromSqlServer());
+    @PreAuthorize("@ss.hasPermi('system:vehicleSync:list')")
+    @GetMapping("/list")
+    public TableDataInfo list() {
+        try {
+            // 1. 浠庢棫绯荤粺鑾峰彇杞﹁締鍒楄〃
+            List<VehicleSyncDTO> oldVehicles = vehicleSyncDataService.getVehiclesFromSqlServer();
+            
+            if (oldVehicles == null || oldVehicles.isEmpty()) {
+                return getDataTable(new ArrayList<>());
+            }
+
+            // 2. 杞崲涓篤O骞舵鏌ュ悓姝ョ姸鎬�
+            List<VehicleSyncVO> voList = new ArrayList<>();
+            for (VehicleSyncDTO dto : oldVehicles) {
+                VehicleSyncVO vo = convertToVO(dto);
+                
+                // 3. 妫�鏌ヨ杞﹁締鏄惁宸插悓姝ュ埌鏂扮郴缁�
+                VehicleInfo existVehicle = vehicleInfoMapper.selectVehicleInfoByCarId(dto.getCarId());
+                if (existVehicle != null) {
+                    vo.setSynced(true);
+                    vo.setVehicleId(existVehicle.getVehicleId());
+                    vo.setDeptId(existVehicle.getDeptId());
+                    if (existVehicle.getDeptName() != null) {
+                        vo.setDeptName(existVehicle.getDeptName());
+                    }
+                } else {
+                    vo.setSynced(false);
+                }
+                
+                voList.add(vo);
+            }
+
+            return getDataTable(voList);
+            
+        } catch (Exception e) {
+            log.error("鏌ヨ杞﹁締鍚屾鍒楄〃澶辫触", e);
+            return getDataTable(new ArrayList<>());
+        }
+    }
+
+    /**
+     * 鎵嬪姩鍚屾鍗曚釜杞﹁締鍒版柊绯荤粺
+     */
+    @PreAuthorize("@ss.hasPermi('system:vehicleSync:sync')")
+    @Log(title = "杞﹁締鍚屾", businessType = BusinessType.INSERT)
+    @PostMapping("/syncVehicle")
+    public AjaxResult syncVehicle(@RequestBody Map<String, Object> params) {
+        try {
+            Integer carId = (Integer) params.get("carId");
+            String vehicleNo = (String) params.get("vehicleNo");
+            Long deptId = params.get("deptId") != null ? Long.valueOf(params.get("deptId").toString()) : null;
+
+            if (carId == null || vehicleNo == null || deptId == null) {
+                return AjaxResult.error("鍙傛暟涓嶅畬鏁达細carId銆乿ehicleNo銆乨eptId 涓嶈兘涓虹┖");
+            }
+
+            // 1. 妫�鏌ユ槸鍚﹀凡瀛樺湪
+            VehicleInfo existVehicle = vehicleInfoMapper.selectVehicleInfoByCarId(carId);
+            if (existVehicle != null) {
+                return AjaxResult.error("璇ヨ溅杈嗗凡鍚屾锛岃溅杈咺D: " + existVehicle.getVehicleId());
+            }
+
+            // 2. 鍒涘缓鏂拌溅杈嗚褰�
+            VehicleInfo newVehicle = new VehicleInfo();
+            newVehicle.setCarId(carId);
+            newVehicle.setVehicleNo(vehicleNo);
+            newVehicle.setDeptId(deptId);
+            newVehicle.setStatus("0"); // 榛樿姝e父鐘舵��
+            newVehicle.setCreateBy(getUsername());
+
+            // 3. 鎻掑叆杞﹁締淇℃伅
+            int result = vehicleInfoMapper.insertVehicleInfo(newVehicle);
+            
+            if (result > 0) {
+                log.info("鎵嬪姩鍚屾杞﹁締鎴愬姛锛歝arId={}, vehicleNo={}, vehicleId={}", 
+                        carId, vehicleNo, newVehicle.getVehicleId());
+                return AjaxResult.success("鍚屾鎴愬姛", newVehicle.getVehicleId());
+            } else {
+                return AjaxResult.error("鍚屾澶辫触");
+            }
+
+        } catch (Exception e) {
+            log.error("鎵嬪姩鍚屾杞﹁締澶辫触", e);
+            return AjaxResult.error("鍚屾澶辫触锛�" + e.getMessage());
+        }
+    }
+
+    /**
+     * 灏� DTO 杞崲涓� VO
+     */
+    private VehicleSyncVO convertToVO(VehicleSyncDTO dto) {
+        VehicleSyncVO vo = new VehicleSyncVO();
+        vo.setCarId(dto.getCarId());
+        vo.setVehicleNo(dto.getCarLicense());
+        vo.setCarOrdClass(dto.getCarOrdClass());
+        
+        // 鏍规嵁carOrdClass鏄犲皠鍒嗗叕鍙稿悕绉�
+        String deptName = mapCarOrdClassToDeptName(dto.getCarOrdClass());
+        vo.setDeptName(deptName);
+        
+        return vo;
+    }
+
+    /**
+     * 鏍规嵁鍗曟嵁绫诲瀷缂栫爜鏄犲皠鍒嗗叕鍙稿悕绉�
+     * 鍙互浠庨厤缃垨鏁版嵁搴撹鍙栵紝杩欓噷绠�鍖栧鐞�
+     */
+    private String mapCarOrdClassToDeptName(String carOrdClass) {
+        // TODO: 鏍规嵁瀹為檯涓氬姟瑙勫垯鏄犲皠
+        // 鍙互浠� sys_config 鎴栦笓闂ㄧ殑鏄犲皠琛ㄨ鍙�
+        Map<String, String> mapping = new HashMap<>();
+        mapping.put("01", "鎬诲叕鍙�");
+        mapping.put("02", "鍒嗗叕鍙窤");
+        mapping.put("03", "鍒嗗叕鍙窧");
+        
+        return mapping.getOrDefault(carOrdClass, "鏈煡鍒嗗叕鍙�");
     }
 }
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 fe67735..56ad818 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
@@ -184,10 +184,9 @@
     @Log(title = "浠诲姟绠$悊", businessType = BusinessType.INSERT)
     @PostMapping("/admin")
     public AjaxResult adminAdd(@RequestBody TaskCreateVO createVO) {
-        return toAjax(sysTaskService.insertSysTask(createVO));
+        Long taskId = sysTaskService.insertSysTask(createVO);
+        return taskId > 0 ? AjaxResult.success("鏂板鎴愬姛").put("taskId", taskId) : AjaxResult.error("鏂板澶辫触");
     }
-
-
 
     /**
      * 鏂板浠诲姟锛圓PP绔級
@@ -195,7 +194,8 @@
     @Log(title = "浠诲姟鍒涘缓", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult appAdd(@RequestBody TaskCreateVO createVO) {
-        return toAjax(sysTaskService.insertSysTask(createVO));
+        Long taskId = sysTaskService.insertSysTask(createVO);
+        return taskId > 0 ? AjaxResult.success("鏂板鎴愬姛").put("taskId", taskId) : AjaxResult.error("鏂板澶辫触");
     }
     
     /**
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index 53c5a19..ffcd5be 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -178,12 +178,17 @@
 tencent:
   map:
     key: 6YVBZ-ZJDLQ-JMY5F-BR7XG-H3TAV-C3FXC
-
+  ocr:
+    secretId: AKID52kDPMUP5WgbGfohKhOvFMGwObYEgtsP
+    secretKey: kLjtpkZDcpnWBcpnY5NQEoAHAoFRN4Wl
 # 鐧惧害鍦板浘閰嶇疆
 baidu:
   map:
     ak: GX7G1RmAbTEQHor9NKpzRiB2jerqaY1E  # 璇锋浛鎹负鎮ㄧ殑鐧惧害鍦板浘API Key
-
+  ocr:
+    appId: 121882692
+    apiKey: zo03Zf3wIU7awOlRnwurDmlO
+    secretKey: tXqyZ5AQ9aT3n1ON4ERQA1aQ88L1sXFw
 # 澶╁湴鍥鹃厤缃�
 tianditu:
   map:
@@ -244,7 +249,11 @@
   qrcode:
     size: 300
     format: PNG
-  
+ali:
+  ocr:
+    accessKeyId: LTAI5t7fbEzL7yctbNzA84Q2
+    accessKeySecret: llHxCvmnhS5YSfRCWeuhr5KxgBTnnz
+
   # 瀵硅处閰嶇疆
   reconciliation:
     enabled: true
diff --git a/ruoyi-admin/src/main/resources/logback.xml b/ruoyi-admin/src/main/resources/logback.xml
index 8c3fc6f..3e0d23e 100644
--- a/ruoyi-admin/src/main/resources/logback.xml
+++ b/ruoyi-admin/src/main/resources/logback.xml
@@ -109,7 +109,7 @@
 		<logger name="org.springframework" level="warn" />
 		<!-- MyBatis SQL鏃ュ織 -->
 		<logger name="com.ruoyi.system.mapper" level="debug" />
-
+		
 		<root level="info">
 			<appender-ref ref="console" />
 			<appender-ref ref="file_info" />
@@ -148,8 +148,15 @@
 		</root>
 	</springProfile>
 	
+	<!-- 榛樿鏍规棩蹇楀櫒閰嶇疆锛堝鏋滄病鏈夋寚瀹歱rofile锛� -->
+	<root level="info">
+		<appender-ref ref="console" />
+		<appender-ref ref="file_info" />
+		<appender-ref ref="file_error" />
+	</root>
+	
 	<!--绯荤粺鐢ㄦ埛鎿嶄綔鏃ュ織-->
     <logger name="sys-user" level="info">
-        <appender-ref ref="sys-user"/>
+        <appender-ref ref="sys-user" />
     </logger>
 </configuration> 
\ No newline at end of file
diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml
index dcb55fa..ced65f2 100644
--- a/ruoyi-common/pom.xml
+++ b/ruoyi-common/pom.xml
@@ -139,6 +139,13 @@
             <scope>provided</scope>
         </dependency>
 
+        <!-- HanLP 涓枃鍒嗚瘝搴� -->
+        <dependency>
+            <groupId>com.hankcs</groupId>
+            <artifactId>hanlp</artifactId>
+            <version>portable-1.8.4</version>
+        </dependency>
+
     </dependencies>
 
 </project>
\ No newline at end of file
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
new file mode 100644
index 0000000..839d971
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/HospitalTokenizerUtil.java
@@ -0,0 +1,698 @@
+package com.ruoyi.common.utils;
+
+import com.hankcs.hanlp.HanLP;
+import com.hankcs.hanlp.seg.common.Term;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.hankcs.hanlp.utility.TextUtility.isChinese;
+
+/**
+ * 鍖婚櫌淇℃伅鍒嗚瘝宸ュ叿绫�
+ * 浣跨敤 HanLP 涓撲笟涓枃鍒嗚瘝搴撹繘琛屽垎璇嶅鐞�
+ * 
+ * @author ruoyi
+ * @date 2026-01-20
+ */
+public class HospitalTokenizerUtil {
+
+    /**
+     * 鍋滅敤璇嶉泦鍚堬紙闇�瑕佽繃婊ょ殑甯歌璇嶆眹锛�
+     * 娉ㄦ剰锛氣�滃尯鈥濄�佲�滀腑鈥濈瓑鍦ㄥ尰闄㈠悕绉颁腑鏈夋剰涔夛紝涓嶅簲杩囨护
+     */
+    private static final Set<String> STOP_WORDS = new HashSet<>(Arrays.asList(
+        "鍖婚櫌", "璇婃墍", "鍗敓", "闀�", "涔�", 
+        "琛楅亾", "璺�", "鍙�", "鏍�", "鍗曞厓", "瀹�", "灞�", "妤�", "鐨�", "浜�", 
+        "鍦�", "涓�", "鍜�", "鍙�", "绛�", "涔�", "浜�", "涓�", "鏈�", "鏃�"
+    ));
+    
+    /**
+     * 楂樻潈閲嶈瘝璇紙鍖荤枟鏈烘瀯鐗瑰緛璇嶏級
+     * 娉ㄦ剰锛氬湴鍖哄悕涓嶅啀鏀惧湪楂樻潈閲嶈瘝涓紝閬垮厤鍒嗛櫌鍥犲寘鍚叾浠栧湴鍖哄悕鑰岃幏寰楅澶栧姞鍒�
+     */
+    private static final Set<String> HIGH_WEIGHT_WORDS = new HashSet<>(Arrays.asList(
+        "浜烘皯", "涓尰", "涓タ鍖�", "涓タ鍖荤粨鍚�", "鍖荤枟", "濡囧辜", "鍎跨", "鑲ょ", 
+        "鍙h厰", "鐪肩", "楠ㄧ", "鏁村舰", "绮剧", "搴峰", "鎬ユ晳", "鍖诲闄�", 
+        "鍖荤澶у", "涓撶", "绗竴", "绗簩", "绗笁", "绗洓", "绗簲",
+        "鍐涘尯", "鍐涘尰", "涓績", "闄勫睘", "鐪佺珛", "甯傜珛", "鍖虹珛"
+    ));
+
+    /**
+     * 鍖婚櫌鍚嶇О鍒嗚瘝鐨勯珮棰戝叧閿瘝瀛楀吀锛堢敤浜庡己鍒舵彁鍙栧畬鏁村尰鐤楃浉鍏崇煭璇級
+     * 浠呭寘鍚尰鐤楁満鏋勭浉鍏宠瘝锛屼笉鍖呭惈鍏蜂綋琛屾斂鍦板悕锛岄伩鍏嶅湴鍖虹‖缂栫爜
+     */
+    private static final Set<String> HOSPITAL_KEYWORD_DICT = new HashSet<>(Arrays.asList(
+        "涓尰闄�", "涓尰鍖婚櫌", "甯傚尰闄�", "鐪佸尰闄�", "浜烘皯鍖婚櫌", "涓績鍖婚櫌", "鍙h厰鍖婚櫌",
+        "鍗庝鲸鍖婚櫌", "鍎跨鍖婚櫌", "鐪肩涓績", "绂忓埄闄�", "闂ㄨ瘖閮�", "涓北澶у", "闄勫睘鍖婚櫌",
+        "瀛欓�镐粰"
+    ));
+
+    /** 缁勫悎璇嶇敓鎴愮殑鏈�灏忓瓧绗﹂暱搴� */
+    private static final int MIN_COMBINED_LEN = 4;
+    /** 缁勫悎璇嶇敓鎴愮殑鏈�澶у瓧绗﹂暱搴� */
+    private static final int MAX_COMBINED_LEN = 30;
+    /** 缁勫悎璇嶇敓鎴愭椂鍖呭惈鐨勬渶澶у垎璇嶆暟閲忥紙娣卞害锛� */
+    private static final int MAX_COMBINED_WORDS = 10;
+
+    /**
+     * 瀵瑰尰闄俊鎭繘琛屽垎璇嶏紙浣跨敤 HanLP锛�
+     * 
+     * @param hospName 鍖婚櫌鍚嶇О
+     * @param hospShort 鍖婚櫌绠�绉�
+     * @param province 鐪佷唤
+     * @param city 鍩庡競
+     * @param area 鍖哄煙
+     * @param address 璇︾粏鍦板潃
+     * @return 鍒嗚瘝缁撴灉锛堥�楀彿鍒嗛殧鐨勫叧閿瘝瀛楃涓诧級
+     */
+    public static String tokenize(String hospName, String hospShort, String province, 
+                                   String city, String area, String address) {
+        Set<String> keywords = new LinkedHashSet<>();
+        
+        // 1. 琛屾斂鍖哄垝锛氬彧浣滀负鐙珛鍏抽敭璇嶏紝涓嶅弬涓庣粍鍚�
+        if (StringUtils.isNotBlank(province)) {
+            keywords.add(province.trim());
+        }
+        if (StringUtils.isNotBlank(city)) {
+            keywords.add(city.trim());
+        }
+        if (StringUtils.isNotBlank(area)) {
+            keywords.add(area.trim());
+        }
+        
+        // 2. 鍖婚櫌鍚嶇О锛氬幓鎺夌渷銆佸競鍓嶇紑锛屽彧瀵光�滃尯+鍖婚櫌涓讳綋鈥濆仛鍒嗚瘝鍜岀粍鍚�
+        if (StringUtils.isNotBlank(hospName)) {
+            String nameForSeg = hospName.trim();
+            
+            // 鍘绘帀鍓嶉潰鐨勭渷浠�
+            if (StringUtils.isNotBlank(province) && nameForSeg.startsWith(province)) {
+                nameForSeg = nameForSeg.substring(province.length());
+            }
+            // 鍐嶅幓鎺夊煄甯�
+            if (StringUtils.isNotBlank(city) && nameForSeg.startsWith(city)) {
+                nameForSeg = nameForSeg.substring(city.length());
+            }
+            // 鍖轰繚鐣欙細渚嬪 "瓒婄鍖轰腑鍖诲尰闄�"锛岃繖鏍峰彲浠ョ敓鎴� "瓒婄鍖轰腑鍖婚櫌"銆�"涓尰闄�" 绛夌粍鍚堣瘝
+            keywords.addAll(extractKeywordsByHanLP(nameForSeg));
+            // 鍩轰簬鍖婚櫌鍏ㄧО锛屽己鍒舵彁鍙栭珮棰戝尰鐤楀叧閿瘝锛堝鈥滀腑鍖婚櫌鈥濃�滃効绔ュ尰闄⑩�濈瓑锛�
+            addDictPhrases(hospName, keywords);
+        }
+        
+        // 3. 鍖婚櫌绠�绉帮細閫氬父涓嶅甫鐪佸競鍖猴紝鐩存帴鍒嗚瘝
+        if (StringUtils.isNotBlank(hospShort)) {
+            keywords.addAll(extractKeywordsByHanLP(hospShort));
+            addDictPhrases(hospShort, keywords);
+        }
+        
+        // 4. 杩囨护鍋滅敤璇嶅拰鏃犳晥璇�
+        keywords = keywords.stream()
+                .filter(keyword -> !STOP_WORDS.contains(keyword))
+                .filter(keyword -> keyword.length() > 0)
+                .filter(HospitalTokenizerUtil::isValidKeyword)
+                .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new));
+        
+        return String.join(",", keywords);
+    }
+
+    /**
+     * 浣跨敤 HanLP 浠庢枃鏈腑鎻愬彇鍏抽敭璇�
+     * 
+     * @param text 鏂囨湰
+     * @return 鍏抽敭璇嶉泦鍚�
+     */
+    private static Set<String> extractKeywordsByHanLP(String text) {
+        Set<String> keywords = new LinkedHashSet<>();
+        
+        if (StringUtils.isBlank(text)) {
+            return keywords;
+        }
+        
+        try {
+            // 浣跨敤 HanLP 杩涜鍒嗚瘝
+            List<Term> terms = HanLP.segment(text.trim());
+            
+            // 娣诲姞瀹屾暣鏂囨湰锛堝鏋滀笉澶暱锛�
+            if (text.length() <= 20) {
+                keywords.add(text.trim());
+            }
+            
+            // 鎻愬彇鍒嗚瘝缁撴灉
+            List<String> validWords = new ArrayList<>();
+            for (Term term : terms) {
+                String word = term.word;
+                
+                // 杩囨护鍗曞瓧绗︼紙闄ら潪鏄噸瑕佺殑涓枃瀛楃锛�
+                if (word.length() == 1 && !isChinese(word.charAt(0))) {
+                    continue;
+                }
+                
+                // 娣诲姞鏈夋晥鐨勫垎璇�
+                if (isValidKeyword(word)) {
+                    keywords.add(word);
+                    validWords.add(word);
+                }
+            }
+            
+            // 銆愬叧閿紭鍖栥�戠敓鎴愯繛缁粍鍚堣瘝
+            // 浣嗚杩囨护鎺夋嫭鍙峰唴瀹癸紝閬垮厤鐢熸垚鏃犳剰涔夌殑鍒嗛櫌缁勫悎璇�
+            // 渚嬪锛歔"瓒婄鍖�", "涓尰", "闄�"] 鈫� 鐢熸垚 "瓒婄鍖轰腑鍖�", "涓尰闄�", "瓒婄鍖轰腑鍖婚櫌"
+            
+            // 绉婚櫎鎷彿鍐呭鐢ㄤ簬鐢熸垚缁勫悎璇�
+            String textWithoutBrackets = text
+                .replaceAll("锛圼^锛塢*锛�", "")  // 绉婚櫎涓枃鎷彿
+                .replaceAll("\\([^\\)]*\\)", "")  // 绉婚櫎鑻辨枃鎷彿
+                .replaceAll("銆怺^銆慮*銆�", "")  // 绉婚櫎鏂规嫭鍙�
+                .trim();
+            
+            // 瀵圭Щ闄ゆ嫭鍙峰悗鐨勬枃鏈噸鏂板垎璇�
+            List<Term> cleanTerms = HanLP.segment(textWithoutBrackets);
+            List<String> cleanValidWords = new ArrayList<>();
+            for (Term term : cleanTerms) {
+                String word = term.word;
+                if (word.length() == 1 && !isChinese(word.charAt(0))) {
+                    continue;
+                }
+                if (isValidKeyword(word)) {
+                    cleanValidWords.add(word);
+                }
+            }
+            
+            // 鍩轰簬骞插噣鐨勫垎璇嶇敓鎴愮粍鍚堣瘝
+            for (int len = 2; len <= Math.min(MAX_COMBINED_WORDS, cleanValidWords.size()); len++) {
+                for (int i = 0; i <= cleanValidWords.size() - len; i++) {
+                    StringBuilder combined = new StringBuilder();
+                    for (int j = i; j < i + len; j++) {
+                        combined.append(cleanValidWords.get(j));
+                    }
+                    String combinedWord = combined.toString();
+                    
+                    // 鍙坊鍔犻暱搴﹀悎鐞嗙殑缁勫悎璇�
+                    if (combinedWord.length() >= MIN_COMBINED_LEN && combinedWord.length() <= MAX_COMBINED_LEN) {
+                        keywords.add(combinedWord);
+                        // 閽堝鈥滆秺绉�鍖轰腑鍖婚櫌鈥濊繖绫绘ā寮忥紝棰濆鐢熸垚鍘绘帀鈥滃尯鈥濈殑绠�鍖栧叧閿瘝锛屽鈥滆秺绉�涓尰闄⑩��
+                        String simplified = simplifyDistrictInKeyword(combinedWord);
+                        if (simplified != null && simplified.length() >= MIN_COMBINED_LEN && simplified.length() <= MAX_COMBINED_LEN) {
+                            keywords.add(simplified);
+                        }
+                    }
+                }
+            }
+            
+        } catch (Exception e) {
+            // HanLP 鍒嗚瘝澶辫触鏃讹紝闄嶇骇浣跨敤绠�鍗曞垎璇�
+            keywords.addAll(extractKeywordsByNGram(text));
+        }
+        
+        return keywords;
+    }
+
+    /**
+     * 闄嶇骇鏂规锛氫娇鐢ㄧ畝鍗曠殑 N-Gram 鍒嗚瘝
+     * 
+     * @param text 鏂囨湰
+     * @return 鍏抽敭璇嶉泦鍚�
+     */
+    private static Set<String> extractKeywordsByNGram(String text) {
+        Set<String> keywords = new LinkedHashSet<>();
+        
+        if (StringUtils.isBlank(text)) {
+            return keywords;
+        }
+        
+        text = text.trim();
+        int length = text.length();
+        
+        // 鐢熸垚2-4瀛楃鐨凬-Gram
+        for (int n = 2; n <= 4 && n <= length; n++) {
+            for (int i = 0; i <= length - n; i++) {
+                String ngram = text.substring(i, i + n);
+                if (isValidKeyword(ngram)) {
+                    keywords.add(ngram);
+                }
+            }
+        }
+        
+        return keywords;
+    }
+
+    /**
+     * 鍒ゆ柇鍏抽敭璇嶆槸鍚︽湁鏁�
+     * 
+     * @param keyword 鍏抽敭璇�
+     * @return 鏄惁鏈夋晥
+     */
+    private static boolean isValidKeyword(String keyword) {
+        if (StringUtils.isBlank(keyword)) {
+            return false;
+        }
+        
+        // 杩囨护绾暟瀛�
+        if (keyword.matches("^\\d+$")) {
+            return false;
+        }
+        
+        // 杩囨护绾鍙�
+        if (keyword.matches("^[\\p{P}\\p{S}]+$")) {
+            return false;
+        }
+        
+        // 鑷冲皯鍖呭惈涓�涓腑鏂囨垨瀛楁瘝
+        return keyword.matches(".*[\\u4e00-\\u9fa5a-zA-Z].*");
+    }
+
+    /**
+     * 閽堝鍚湁鈥滃尯涓尰闄�/鍖轰腑鍖烩�濈殑缁勫悎璇嶏紝鐢熸垚鍘绘帀鈥滃尯鈥濈殑绠�鍖栧舰寮�
+     * 渚嬪锛�"瓒婄鍖轰腑鍖婚櫌" 鈫� "瓒婄涓尰闄�"锛�"瓒婄鍖轰腑鍖�" 鈫� "瓒婄涓尰"
+     */
+    private static String simplifyDistrictInKeyword(String keyword) {
+        if (StringUtils.isBlank(keyword)) {
+            return null;
+        }
+        // 閫氱敤瑙勫垯锛氬幓鎺夆�滃尯鈥濊繖涓鏀垮眰绾ф爣璇嗭紝浣嗕粎闄愪簬鈥滃尯涓尰闄�/鍖轰腑鍖烩�濊繖绉嶅尰鐤楀満鏅�
+        if (keyword.contains("鍖轰腑鍖婚櫌")) {
+            return keyword.replaceFirst("鍖轰腑鍖婚櫌", "涓尰闄�");
+        }
+        if (keyword.contains("鍖轰腑鍖�")) {
+            return keyword.replaceFirst("鍖轰腑鍖�", "涓尰");
+        }
+        return null;
+    }
+
+    /**
+     * 鍩轰簬鍖婚櫌鍚嶇О/绠�绉帮紝寮哄埗鎻愬彇鍖婚櫌鍏抽敭璇嶅瓧鍏镐腑鐨勭煭璇�
+     */
+    private static void addDictPhrases(String text, Set<String> keywords) {
+        if (StringUtils.isBlank(text) || keywords == null) {
+            return;
+        }
+        for (String phrase : HOSPITAL_KEYWORD_DICT) {
+            if (text.contains(phrase)) {
+                keywords.add(phrase);
+            }
+        }
+    }
+    
+     /* 绉婚櫎鍖婚櫌鍚嶇О涓殑鍦板煙鍓嶇紑锛堢渷/甯�/鑷不鍖虹瓑锛�
+     * 閫氱敤澶勭悊锛屼笉纭紪鐮佸叿浣撳湴鍚�
+     * 
+     * @param hospName 鍖婚櫌鍚嶇О
+     * @return 绉婚櫎鍦板煙鍓嶇紑鍚庣殑鍚嶇О
+     */
+    private static String removeLocationPrefixes(String hospName) {
+        if (StringUtils.isBlank(hospName)) {
+            return hospName;
+        }
+        
+        String result = hospName;
+        
+        // 绉婚櫎甯歌鐨勮鏀垮尯鍒掑悗缂�
+        // 鐪佺骇锛� XX鐪併�乆X甯傦紙鐩磋緰甯傦級銆乆X鑷不鍖�
+        result = result.replaceFirst("^[\\u4e00-\\u9fa5]{2,10}鐪�", "");
+        result = result.replaceFirst("^[\\u4e00-\\u9fa5]{2,10}鑷不鍖�", "");
+        
+        // 鍦扮骇甯傦細XX甯�
+        result = result.replaceFirst("^[\\u4e00-\\u9fa5]{2,10}甯�", "");
+        
+        // 鍘跨骇锛歑X鍖恒�乆X鍘裤�乆X甯傦紙鍘跨骇甯傦級
+        result = result.replaceFirst("^[\\u4e00-\\u9fa5]{2,10}鍖�", "");
+        result = result.replaceFirst("^[\\u4e00-\\u9fa5]{2,10}鍘�", "");
+        
+        return result.trim();
+    }
+
+    /**
+     * 璁$畻涓や釜鍒嗚瘝闆嗗悎鐨勫尮閰嶅害锛堜紭鍖栫増锛�
+     * 鑰冭檻鍥犵礌锛�
+     * 0. 銆愭牳蹇冦�戝畬鏁存悳绱㈡枃鏈湪keywords涓瓨鍦� 鈫� 楂樺垎锛�+100鍒嗭級
+     * 1. 瀹屾暣鍖归厤鍔犲垎锛堝崟涓瘝鍖归厤锛�
+     * 1.5 瓒呯骇鍔犲垎锛氬畬鏁存悳绱㈡枃鏈寘鍚湪鍖婚櫌鍚嶄腑锛�+80鍒嗭級锛屾湭鍖归厤鍐呭娓愯繘鎯╃綒
+     * 2. 璇嶈鏉冮噸锛堥噸瑕佽瘝姹囧姞鍒嗭級
+     * 3. 杩炵画鍖归厤鍔犲垎
+     * 4. 瀛楃鐩镐技搴�
+     * 5. 璐熷悜鍖归厤鎯╃綒锛堝尰闄㈠悕涓嚭鐜版悳绱㈣瘝涔嬪鐨勫湴鍖哄悕 -30鍒嗭級
+     * 6. 鍒嗛櫌杞诲井闄嶆潈锛�-10鍒嗭級
+     * 7. 鎷彿鍐呭杞诲井鎯╃綒锛�-5鍒嗭級
+     * 
+     * @param searchKeywords 鎼滅储鍒嗚瘝锛堥�楀彿鍒嗛殧锛�
+     * @param hospitalKeywords 鍖婚櫌鍒嗚瘝锛堥�楀彿鍒嗛殧锛�
+     * @param hospName 鍖婚櫌鍚嶇О锛堢敤浜庡畬鏁村尮閰嶅垽鏂級
+     * @param districtNames 鍦板尯鍚嶇О闆嗗悎锛堢敤浜庤礋鍚戝尮閰嶆鏌ワ紝鍙负null锛�
+     * @return 鍖归厤鍒嗘暟
+     */
+    public static int calculateMatchScore(String searchKeywords, String hospitalKeywords, String hospName, Set<String> districtNames) {
+        if (StringUtils.isBlank(searchKeywords) || StringUtils.isBlank(hospitalKeywords)) {
+            return 0;
+        }
+        
+        List<String> searchWords = Arrays.asList(searchKeywords.split(","));
+        List<String> hospWords = Arrays.asList(hospitalKeywords.split(","));
+        Set<String> searchWordsSet = new HashSet<>(searchWords);
+        Set<String> hospWordsSet = new HashSet<>(hospWords);
+        
+        int totalScore = 0;
+        
+        // 0. 銆愭牳蹇冧紭鍖栥�戦鍏堝垽鏂槸鍚﹀瓨鍦ㄢ�滃畬鏁村尮閰嶁��
+        // 绾﹀畾锛歴earchKeywords 鐨勭涓�涓垎璇嶄负鍘熷鎼滅储鏂囨湰
+        String fullSearchText = searchWords.get(0);
+        boolean keywordFullMatch = hospWordsSet.contains(fullSearchText);
+        boolean nameFullMatch = (hospName != null && hospName.contains(fullSearchText));
+        
+        if (keywordFullMatch || nameFullMatch) {
+            // 瀹屾暣鍖归厤浼樺厛锛氱洿鎺ョ粰鍥哄畾鏋侀珮鍒嗭紝纭繚鎺掑湪鏈�鍓嶉潰
+            totalScore = 1000; // 鎻愬崌鍩虹鍒嗕负1000锛屼綔涓哄垎鏁板ぉ鑺辨澘
+            
+            // 瀵瑰畬鏁村尮閰嶇粨鏋滐紝浠嶇劧鍙互搴旂敤鍦板尯鎯╃綒鍜屽垎闄�/鎷彿杞诲井闄嶆潈锛屼繚璇佽涔夋纭�
+            if (districtNames != null && !districtNames.isEmpty()) {
+                totalScore -= calculateNegativeMatchPenalty(searchWordsSet, districtNames, hospName);
+            }
+            
+//            if (isBranchHospital(hospName)) {
+//                totalScore -= 10;  // 鍒嗛櫌鎵�10鍒�
+//            }
+            
+//            if (hospName != null && (hospName.contains("锛�") || hospName.contains("(") || hospName.contains("銆�"))) {
+//                totalScore -= 5;   // 鎷彿鍐呭杞诲井鎵e垎
+//            }
+            
+            return Math.max(0, totalScore);
+        }
+        
+        // 1. 瀹屾暣鍖归厤鍔犲垎锛堝崟涓瘝鍖归厤锛�
+        for (String searchWord : searchWords) {
+            if (searchWord.length() >= 4 && hospName != null && hospName.contains(searchWord)) {
+                totalScore += 50;  // 瀹屾暣璇嶅尮閰嶅姞鍒�
+            }
+        }
+        
+        // 1.5 瓒呯骇鍔犲垎锛氭悳绱㈡枃鏈笌鍖婚櫌鍚嶇殑瀹屾暣鐩镐技搴�
+        if (hospName != null) {
+            // 瀹屽叏鍖呭惈鍔犲垎
+            if (hospName.contains(fullSearchText)) {
+                totalScore += 500;  // 鎻愬崌鍖呭惈鍏崇郴鐨勫垎鏁帮紝纭繚鍖呭惈鎼滅储鍏ㄧО鐨勭粨鏋滄帓鍚嶉潬鍓�
+            } else {
+                // 璁$畻鏁翠綋鐩镐技搴�
+                int similarity = calculateStringSimilarity(fullSearchText, hospName);
+                if (similarity > 80) {
+                    totalScore += similarity / 2;  // 楂樺害鐩镐技涔熷姞鍒嗭紝浣嗘潈閲嶉檷浣�
+                }
+            }
+            
+            // 鏈尮閰嶅唴瀹规笎杩涙儵缃氾細鍖婚櫌鍚嶄腑鏈夋悳绱㈣瘝涔嬪鐨勫唴瀹�
+            String cleanedHospName = removeLocationPrefixes(hospName);
+            int unmatchedLength = cleanedHospName.length() - fullSearchText.length();
+            if (unmatchedLength > 0) {
+                // 娓愯繘鎯╃綒锛�1-5瀛楁墸1鍒�/瀛楋紝6-10瀛楁墸2鍒�/瀛楋紝11+瀛楁墸3鍒�/瀛�
+                if (unmatchedLength <= 5) {
+                    totalScore -= unmatchedLength * 1;
+                } else if (unmatchedLength <= 10) {
+                    totalScore -= 5 + (unmatchedLength - 5) * 2;
+                } else {
+                    totalScore -= 5 + 10 + (unmatchedLength - 10) * 3;
+                }
+            }
+        }
+        
+        // 2. 鍒嗚瘝鍖归厤璁″垎锛堜紭鍏堝尮閰嶈緝闀跨殑鎼滅储璇嶏紝鍛戒腑鍗虫锛�
+        List<String> sortedSearchWords = new ArrayList<>(searchWords);
+        sortedSearchWords.sort((a, b) -> Integer.compare(b.length(), a.length())); // 鎸夐暱搴︿粠闀垮埌鐭�
+        boolean anyMatch = false;
+        
+        for (String searchWord : sortedSearchWords) {
+            boolean isLong = searchWord.length() >= 4;
+            if (hospWords.contains(searchWord)) {
+                int wordScore;
+                if (isLong) {
+                    // 闀胯瘝瀹屾暣鍖归厤锛氶珮鍒�
+                    wordScore = 40 + searchWord.length() * 4;
+                } else {
+                    // 鐭瘝瀹屾暣鍖归厤锛氫綆鍒�
+                    wordScore = 10 + searchWord.length() * 2;
+                }
+                
+                // 楂樻潈閲嶈瘝棰濆鍔犲垎
+                if (HIGH_WEIGHT_WORDS.contains(searchWord)) {
+                    wordScore += 15;
+                }
+                
+                totalScore += wordScore;
+                anyMatch = true;
+                
+                // 銆愭牳蹇冧慨鏀广�戝彧瑕佸尮閰嶅埌涓�涓垎璇嶏紙鏃犺闀跨煭锛夛紝灏变腑鏂悗缁尮閰嶏紝閬靛惊闀胯瘝浼樺厛鍘熷垯
+                break;
+            } else {
+                // 2.3 閮ㄥ垎鍖归厤锛堝寘鍚叧绯伙級锛屽彧瀵硅緝闀挎悳绱㈣瘝鑰冭檻
+                if (isLong) {
+                    for (String hospWord : hospWords) {
+                        if (hospWord.contains(searchWord) || searchWord.contains(hospWord)) {
+                            int partialScore = Math.min(searchWord.length(), hospWord.length()) * 2;
+                            totalScore += partialScore;
+                            anyMatch = true;
+                            break;
+                        }
+                    }
+                    if (anyMatch) {
+                        break; // 鍛戒腑鍗虫
+                    }
+                }
+            }
+        }
+        
+        // 濡傛灉宸茬粡鏈夊尮閰嶏紝鍒欏簲鐢ㄨ礋鍚戞儵缃氥�佸垎闄�/鎷彿璋冩暣骞惰繑鍥�
+        if (anyMatch) {
+            if (districtNames != null && !districtNames.isEmpty()) {
+                totalScore -= calculateNegativeMatchPenalty(searchWordsSet, districtNames, hospName);
+            }
+            if (isBranchHospital(hospName)) {
+                totalScore -= 10;
+            }
+            if (hospName != null && (hospName.contains("锛�") || hospName.contains("(") || hospName.contains("銆�"))) {
+                totalScore -= 5;
+            }
+            return Math.max(0, totalScore);
+        }
+        
+        // 3. 杩炵画鍖归厤鍔犲垎
+        totalScore += calculateContinuousMatchBonus(searchWords, hospWords);
+        
+        // 4. 瀛楃鐩镐技搴﹀姞鍒嗭紙瀵逛簬闀胯瘝锛�
+        for (String searchWord : searchWords) {
+            if (searchWord.length() >= 4) {
+                for (String hospWord : hospWords) {
+                    if (hospWord.length() >= 4) {
+                        int similarity = calculateStringSimilarity(searchWord, hospWord);
+                        if (similarity > 70) {  // 鐩镐技搴﹁秴杩�70%
+                            totalScore += similarity / 10;
+                        }
+                    }
+                }
+            }
+        }
+        
+        // 5. 璐熷悜鍖归厤鎯╃綒锛氬尰闄㈠悕涓寘鍚悳绱㈣瘝涔嬪鐨勯珮鏉冮噸鍦板尯鍚�
+        if (districtNames != null && !districtNames.isEmpty()) {
+            totalScore -= calculateNegativeMatchPenalty(searchWordsSet, districtNames, hospName);
+        }
+        
+        // 6. 鍒嗛櫌杞诲井闄嶆潈锛氫富闄紭鍏堬紝浣嗕笉瑕佽繃搴︽儵缃�
+        if (isBranchHospital(hospName)) {
+            totalScore -= 10;  // 鍒嗛櫌鎵�10鍒嗭紙鏀逛负鍥哄畾鎵e垎锛岃�岄潪鎵撴姌锛�
+        }
+        
+        // 7. 鎷彿鍐呭杞诲井鎯╃綒锛氭嫭鍙峰唴閫氬父鏄瑕佷俊鎭�
+        if (hospName != null && (hospName.contains("锛�") || hospName.contains("(") || hospName.contains("銆�"))) {
+            totalScore -= 5;  // 鍖呭惈鎷彿鎵�5鍒嗭紙鏀逛负鍥哄畾鎵e垎锛�
+        }
+        
+        return Math.max(0, totalScore);  // 纭繚鍒嗘暟涓嶄负璐�
+    }
+    
+    /**
+     * 璁$畻璐熷悜鍖归厤鎯╃綒
+     * 濡傛灉鍖婚櫌鍚嶄腑鍖呭惈鎼滅储璇嶄箣澶栫殑鍦板尯鍚嶇О锛屽簲璇ラ檷浣庢帓鍚�
+     * 
+     * @param searchWords 鎼滅储璇嶉泦鍚�
+     * @param districtNames 鎵�鏈夊尰闄㈢殑鍦板尯鍚嶇О闆嗗悎锛堜粠鍖婚櫌琛ㄧ殑 hopsArea 瀛楁鎻愬彇锛�
+     * @param hospName 褰撳墠鍖婚櫌鍚嶇О
+     * @return 鎯╃綒鍒嗘暟
+     */
+    private static int calculateNegativeMatchPenalty(Set<String> searchWords, Set<String> districtNames, String hospName) {
+        if (hospName == null || districtNames == null || districtNames.isEmpty()) {
+            return 0;
+        }
+        
+        int penalty = 0;
+        
+        // 妫�鏌ュ尰闄㈠悕涓殑鍦板尯鍚�
+        for (String district : districtNames) {
+            if (StringUtils.isBlank(district)) {
+                continue;
+            }
+            
+            // 濡傛灉鍖婚櫌鍚嶅寘鍚鍦板尯鍚�
+            if (hospName.contains(district)) {
+                // 妫�鏌ユ槸鍚﹀湪鎼滅储璇嶄腑鍑虹幇
+                boolean inSearchWords = false;
+                
+                // 1. 鐩存帴鍖归厤锛氭悳绱㈣瘝闆嗗悎涓寘鍚鍦板尯鍚�
+                if (searchWords.contains(district)) {
+                    inSearchWords = true;
+                } else {
+                    // 2. 閮ㄥ垎鍖归厤锛氭悳绱㈣瘝鐨勪换浣曚竴涓瘝鍖呭惈璇ュ湴鍖哄悕
+                    for (String searchWord : searchWords) {
+                        if (searchWord.contains(district)) {
+                            inSearchWords = true;
+                            break;
+                        }
+                    }
+                }
+                
+                // 濡傛灉鍖婚櫌鍚嶅寘鍚鍦板尯鍚嶏紝浣嗘悳绱㈣瘝涓病鏈夛紝鍒欐墸鍒�
+                if (!inSearchWords) {
+                    penalty += 30;  // 鍖呭惈涓嶇浉鍏冲湴鍖哄悕锛屾墸30鍒�
+                }
+            }
+        }
+        
+        return penalty;
+    }
+    
+    /**
+     * 鍒ゆ柇鏄惁涓哄垎闄�
+     */
+    private static boolean isBranchHospital(String hospName) {
+        if (hospName == null) {
+            return false;
+        }
+        
+        // 鍒嗛櫌鐗瑰緛鍏抽敭璇�
+        String[] branchKeywords = {
+            "鍒嗛櫌", "鍒嗛儴", "闂ㄨ瘖閮�", "绀惧尯鍗敓", "鍗敓绔�", "鍗敓鏈嶅姟涓績",
+            "涓滈櫌", "瑗块櫌", "鍗楅櫌", "鍖楅櫌", "鏂伴櫌", "鑰侀櫌"
+        };
+        
+        for (String keyword : branchKeywords) {
+            if (hospName.contains(keyword)) {
+                return true;
+            }
+        }
+        
+        // 鍖呭惈鍏蜂綋璺悕/琛楅亾鍚嶄篃鍙兘鏄垎闄�
+        String[] roadKeywords = {
+            "璺垎闄�", "琛楀垎闄�", "閬撳垎闄�", "澶ч亾鍒嗛櫌"
+        };
+        
+        for (String keyword : roadKeywords) {
+            if (hospName.contains(keyword)) {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+    
+    /**
+     * 璁$畻杩炵画鍖归厤鍔犲垎
+     */
+    private static int calculateContinuousMatchBonus(List<String> searchWords, List<String> hospWords) {
+        int bonus = 0;
+        int consecutiveCount = 0;
+        
+        for (int i = 0; i < searchWords.size() - 1; i++) {
+            String word1 = searchWords.get(i);
+            String word2 = searchWords.get(i + 1);
+            
+            // 鍒ゆ柇鏄惁鍦ㄥ尰闄㈠垎璇嶄腑杩炵画鍑虹幇
+            boolean found = false;
+            for (int j = 0; j < hospWords.size() - 1; j++) {
+                if (hospWords.get(j).equals(word1) && hospWords.get(j + 1).equals(word2)) {
+                    consecutiveCount++;
+                    found = true;
+                    break;
+                }
+            }
+            
+            if (found) {
+                bonus += consecutiveCount * 5;  // 杩炵画瓒婇暱鍔犲垎瓒婂
+            } else {
+                consecutiveCount = 0;
+            }
+        }
+        
+        return bonus;
+    }
+    
+    /**
+     * 璁$畻瀛楃涓茬浉浼煎害锛堜娇鐢↙evenshtein璺濈锛�
+     * 
+     * @param s1 瀛楃涓�1
+     * @param s2 瀛楃涓�2
+     * @return 鐩镐技搴︾櫨鍒嗘瘮 (0-100)
+     */
+    private static int calculateStringSimilarity(String s1, String s2) {
+        if (s1.equals(s2)) {
+            return 100;
+        }
+        
+        int maxLen = Math.max(s1.length(), s2.length());
+        if (maxLen == 0) {
+            return 100;
+        }
+        
+        int distance = levenshteinDistance(s1, s2);
+        return (int) ((1 - (double) distance / maxLen) * 100);
+    }
+    
+    /**
+     * 璁$畻Levenshtein璺濈锛堢紪杈戣窛绂伙級
+     */
+    private static int levenshteinDistance(String s1, String s2) {
+        int len1 = s1.length();
+        int len2 = s2.length();
+        
+        int[][] dp = new int[len1 + 1][len2 + 1];
+        
+        for (int i = 0; i <= len1; i++) {
+            dp[i][0] = i;
+        }
+        
+        for (int j = 0; j <= len2; j++) {
+            dp[0][j] = j;
+        }
+        
+        for (int i = 1; i <= len1; i++) {
+            for (int j = 1; j <= len2; j++) {
+                int cost = s1.charAt(i - 1) == s2.charAt(j - 1) ? 0 : 1;
+                dp[i][j] = Math.min(
+                    Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1),
+                    dp[i - 1][j - 1] + cost
+                );
+            }
+        }
+        
+        return dp[len1][len2];
+    }
+
+    /**
+     * 瀵规枃鏈繘琛屽垎璇嶏紙鍓嶇浼犲叆鐨勬悳绱㈠叧閿瘝锛�
+     * 
+     * @param text 鎼滅储鏂囨湰
+     * @return 鍒嗚瘝缁撴灉锛堥�楀彿鍒嗛殧锛�
+     */
+    public static String tokenizeSearchText(String text) {
+        if (StringUtils.isBlank(text)) {
+            return "";
+        }
+        
+        Set<String> keywords = extractKeywordsByHanLP(text.trim());
+        
+        // 杩囨护鍋滅敤璇�
+        keywords = keywords.stream()
+                .filter(keyword -> !STOP_WORDS.contains(keyword))
+                .filter(keyword -> keyword.length() > 0)
+                .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new));
+        
+        return String.join(",", keywords);
+    }
+}
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacySystemSyncTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacySystemSyncTask.java
index ba6caaf..72c8e29 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacySystemSyncTask.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacySystemSyncTask.java
@@ -13,7 +13,7 @@
 import com.ruoyi.system.service.ITaskStatusPushService;
 
 /**
- * 鏃х郴缁熷悓姝ュ畾鏃朵换鍔�
+ * 鏃х郴缁熷悓姝ュ畾鏃朵换鍔� 鏂扮郴缁熶腑鐨勪换鍔″悓姝ュ埌鏃х郴缁熶腑
  * 
  * @author ruoyi
  * @date 2024-01-20
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacyTransferSyncTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacyTransferSyncTask.java
index 9e27a30..71c1435 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacyTransferSyncTask.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacyTransferSyncTask.java
@@ -8,7 +8,7 @@
 
 /**
  * 鏃х郴缁熻浆杩愬崟鍚屾瀹氭椂浠诲姟
- * 
+ *  (鏃х郴缁熻縼绉诲埌鏂扮郴缁�)
  * @author ruoyi
  * @date 2025-11-19
  */
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleAbnormalAlertTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleAbnormalAlertTask.java
new file mode 100644
index 0000000..8576d4d
--- /dev/null
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleAbnormalAlertTask.java
@@ -0,0 +1,547 @@
+package com.ruoyi.quartz.task;
+
+import com.ruoyi.common.core.domain.entity.SysDept;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.system.domain.*;
+import com.ruoyi.system.mapper.*;
+import com.ruoyi.system.service.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 杞﹁締寮傚父杩愯鐩戞帶瀹氭椂浠诲姟
+ * 
+ * @author ruoyi
+ */
+@Component("vehicleAbnormalAlertTask")
+public class VehicleAbnormalAlertTask {
+    
+    private static final Logger log = LoggerFactory.getLogger(VehicleAbnormalAlertTask.class);
+
+    @Autowired
+    private ISysConfigService configService;
+
+    @Autowired
+    private VehicleGpsSegmentMileageMapper segmentMileageMapper;
+
+    @Autowired
+    private VehicleInfoMapper vehicleInfoMapper;
+
+    @Autowired
+    private SysTaskMapper sysTaskMapper;
+
+    @Autowired
+    private VehicleAbnormalAlertMapper alertMapper;
+
+    @Autowired
+    private IQyWechatService qyWechatService;
+
+    @Autowired
+    private ISysDeptService deptService;
+
+    @Autowired
+    private IVehicleAbnormalAlertService alertService;
+
+    @Autowired
+    private IVehicleAlertConfigService alertConfigService;
+
+    @Autowired
+    private ISysUserService userService;
+
+    /**
+     * 鐩戞帶杞﹁締寮傚父杩愯鎯呭喌
+     */
+    public void monitorVehicleAbnormalRunning() {
+        try {
+            // 妫�鏌ュ姛鑳藉紑鍏�
+            if (!isAlertEnabled()) {
+                log.debug("杞﹁締寮傚父鍛婅鍔熻兘鏈惎鐢紝璺宠繃鐩戞帶");
+                return;
+            }
+
+            log.info("寮�濮嬫墽琛岃溅杈嗗紓甯歌繍琛岀洃鎺т换鍔�");
+            
+            // 鍔犺浇閰嶇疆鍙傛暟
+            AlertConfig config = loadAlertConfig();
+            
+            // 鑾峰彇鐩戞帶鏃堕棿绐楀彛
+            Date endTime = new Date();
+            Calendar cal = Calendar.getInstance();
+            cal.setTime(endTime);
+            cal.add(Calendar.MINUTE, -config.timeWindow);
+            Date startTime = cal.getTime();
+            
+            // 鏌ヨ鎵�鏈夋椿璺冭溅杈�
+            List<VehicleInfo> vehicles = vehicleInfoMapper.selectVehicleInfoList(new VehicleInfo());
+            if (vehicles == null || vehicles.isEmpty()) {
+                log.debug("娌℃湁鎵惧埌闇�瑕佺洃鎺х殑杞﹁締");
+                return;
+            }
+            
+            log.info("寮�濮嬬洃鎺� {} 杈嗚溅杈嗭紝鏃堕棿绐楀彛: {} 鍒� {}", vehicles.size(), 
+                    DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, startTime),
+                    DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, endTime));
+            
+            int alertCount = 0;
+            for (VehicleInfo vehicle : vehicles) {
+                try {
+                    if (checkVehicleAbnormalRunning(vehicle, startTime, endTime, config)) {
+                        alertCount++;
+                    }
+                } catch (Exception e) {
+                    log.error("妫�鏌ヨ溅杈� {} 寮傚父杩愯澶辫触", vehicle.getVehicleNo(), e);
+                }
+            }
+            
+            log.info("杞﹁締寮傚父杩愯鐩戞帶浠诲姟瀹屾垚锛屽叡浜х敓 {} 涓憡璀�", alertCount);
+            
+        } catch (Exception e) {
+            log.error("杞﹁締寮傚父杩愯鐩戞帶浠诲姟鎵ц澶辫触", e);
+        }
+    }
+
+    /**
+     * 妫�鏌ュ崟涓溅杈嗘槸鍚﹀紓甯歌繍琛�
+     */
+    private boolean checkVehicleAbnormalRunning(VehicleInfo vehicle, Date startTime, Date endTime, AlertConfig globalConfig) {
+        Long vehicleId = vehicle.getVehicleId();
+        String vehicleNo = vehicle.getVehicleNo();
+        Long deptId = vehicle.getDeptId();
+        
+        // 鑾峰彇璇ヨ溅杈嗙殑閰嶇疆锛堜紭鍏堢骇锛氳溅杈� > 閮ㄩ棬 > 鍏ㄥ眬锛�
+        AlertConfig config = getVehicleAlertConfig(vehicleId, deptId, globalConfig);
+        if (config == null) {
+            log.debug("杞﹁締 {} 鏈壘鍒版湁鏁堥厤缃紝璺宠繃鐩戞帶", vehicleNo);
+            return false;
+        }
+        
+        // 1. 鏌ヨ杞﹁締鍦ㄦ椂闂寸獥鍙e唴鐨勬�昏繍琛岄噷绋�
+        BigDecimal totalMileage = calculateVehicleMileage(vehicleId, startTime, endTime);
+        if (totalMileage == null || totalMileage.compareTo(BigDecimal.ZERO) == 0) {
+            log.debug("杞﹁締 {} 鍦ㄧ洃鎺х獥鍙e唴鏃犺繍琛岄噷绋�", vehicleNo);
+            return false;
+        }
+        
+        // 2. 鏌ヨ杞﹁締鍦ㄦ椂闂寸獥鍙e唴鏈変换鍔℃椂鐨勯噷绋�
+        BigDecimal taskMileage = calculateTaskMileage(vehicleId, startTime, endTime);
+        if (taskMileage == null) {
+            taskMileage = BigDecimal.ZERO;
+        }
+        
+        // 3. 璁$畻闈炰换鍔$姸鎬佷笅鐨勮繍琛岄噷绋�
+        BigDecimal nonTaskMileage = totalMileage.subtract(taskMileage);
+        if (nonTaskMileage.compareTo(BigDecimal.ZERO) <= 0) {
+            log.debug("杞﹁締 {} 鍦ㄧ洃鎺х獥鍙e唴鏃犻潪浠诲姟閲岀▼", vehicleNo);
+            return false;
+        }
+        
+        log.debug("杞﹁締 {} 鎬婚噷绋�: {}km, 浠诲姟閲岀▼: {}km, 闈炰换鍔¢噷绋�: {}km", 
+                vehicleNo, totalMileage, taskMileage, nonTaskMileage);
+        
+        // 4. 妫�鏌ラ潪浠诲姟閲岀▼鏄惁瓒呰繃鍏噷鏁伴槇鍊�
+        if (nonTaskMileage.compareTo(config.mileageThreshold) <= 0) {
+            log.debug("杞﹁締 {} 闈炰换鍔¤繍琛岄噷绋� {}km 鏈秴杩囬槇鍊� {}km", 
+                    vehicleNo, nonTaskMileage, config.mileageThreshold);
+            return false;
+        }
+        
+        // 5. 妫�鏌ュ憡璀﹂鐜囬檺鍒�
+        if (!checkAlertFrequency(vehicleId, config)) {
+            log.debug("杞﹁締 {} 宸茶揪鍒板憡璀﹂鐜囬檺鍒�", vehicleNo);
+            return false;
+        }
+        
+        // 6. 鍒涘缓鍛婅璁板綍
+        return createAlertAndNotify(vehicle, nonTaskMileage, startTime, endTime, config);
+    }
+
+    /**
+     * 璁$畻杞﹁締杩愯閲岀▼
+     */
+    private BigDecimal calculateVehicleMileage(Long vehicleId, Date startTime, Date endTime) {
+        try {
+            // 鏌ヨ杞﹁締鍦ㄦ椂闂寸獥鍙e唴鐨勫垎娈甸噷绋嬭褰�
+            VehicleGpsSegmentMileage query = new VehicleGpsSegmentMileage();
+            query.setVehicleId(vehicleId);
+            
+            Map<String, Object> params = new HashMap<>();
+            params.put("vehicleId", vehicleId);
+            params.put("startTime", startTime);
+            params.put("endTime", endTime);
+            
+            List<VehicleGpsSegmentMileage> segments = segmentMileageMapper.selectSegmentsByTimeRange(params);
+            
+            if (segments == null || segments.isEmpty()) {
+                return BigDecimal.ZERO;
+            }
+            
+            // 绱姞鎵�鏈夊垎娈甸噷绋�
+            BigDecimal totalMileage = segments.stream()
+                    .map(VehicleGpsSegmentMileage::getSegmentDistance)
+                    .filter(Objects::nonNull)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            
+            return totalMileage;
+            
+        } catch (Exception e) {
+            log.error("璁$畻杞﹁締閲岀▼澶辫触锛寁ehicleId={}", vehicleId, e);
+            return BigDecimal.ZERO;
+        }
+    }
+
+    /**
+     * 璁$畻杞﹁締鍦ㄦ湁浠诲姟鏃剁殑杩愯閲岀▼
+     */
+    private BigDecimal calculateTaskMileage(Long vehicleId, Date startTime, Date endTime) {
+        try {
+            // 1. 鏌ヨ杞﹁締鍦ㄦ椂闂寸獥鍙e唴鐨勪换鍔�
+            Map<String, Object> taskParams = new HashMap<>();
+            taskParams.put("vehicleId", vehicleId);
+            taskParams.put("startTime", startTime);
+            taskParams.put("endTime", endTime);
+            
+            List<SysTask> tasks = sysTaskMapper.selectVehicleTasksInTimeRange(taskParams);
+            
+            if (tasks == null || tasks.isEmpty()) {
+                return BigDecimal.ZERO;
+            }
+            
+            // 鎺掗櫎宸插彇娑堢殑浠诲姟
+            List<SysTask> activeTasks = tasks.stream()
+                    .filter(t -> !"CANCELLED".equals(t.getTaskStatus()))
+                    .collect(Collectors.toList());
+            
+            if (activeTasks.isEmpty()) {
+                return BigDecimal.ZERO;
+            }
+            
+            // 2. 鏌ヨ杞﹁締鐨勬墍鏈塆PS鍒嗘閲岀▼
+            Map<String, Object> segmentParams = new HashMap<>();
+            segmentParams.put("vehicleId", vehicleId);
+            segmentParams.put("startTime", startTime);
+            segmentParams.put("endTime", endTime);
+            
+            List<VehicleGpsSegmentMileage> segments = segmentMileageMapper.selectSegmentsByTimeRange(segmentParams);
+            
+            if (segments == null || segments.isEmpty()) {
+                return BigDecimal.ZERO;
+            }
+            
+            // 3. 绛涢�夊嚭鍦ㄤ换鍔℃椂闂磋寖鍥村唴鐨勫垎娈甸噷绋�
+            BigDecimal taskMileage = BigDecimal.ZERO;
+            
+            for (VehicleGpsSegmentMileage segment : segments) {
+                Date segmentStart = segment.getSegmentStartTime();
+                Date segmentEnd = segment.getSegmentEndTime();
+                
+                if (segmentStart == null || segmentEnd == null) {
+                    continue;
+                }
+                
+                // 妫�鏌ヨ鍒嗘鏄惁鍦ㄤ换鎰忎换鍔$殑鏃堕棿鑼冨洿鍐�
+                for (SysTask task : activeTasks) {
+                    Date taskStart = task.getPlannedStartTime();
+                    Date taskEnd = task.getActualEndTime();
+                    
+                    // 濡傛灉浠诲姟杩樻病瀹屾垚锛屼娇鐢ㄥ綋鍓嶆椂闂翠綔涓虹粨鏉熸椂闂�
+                    if (taskEnd == null) {
+                        taskEnd = endTime;
+                    }
+                    
+                    if (taskStart == null) {
+                        continue;
+                    }
+                    
+                    // 鍒ゆ柇鍒嗘鏃堕棿鏄惁涓庝换鍔℃椂闂存湁閲嶅彔
+                    if (isTimeOverlap(segmentStart, segmentEnd, taskStart, taskEnd)) {
+                        if (segment.getSegmentDistance() != null) {
+                            taskMileage = taskMileage.add(segment.getSegmentDistance());
+                        }
+                        break; // 璇ュ垎娈靛凡璁″叆浠诲姟閲岀▼锛屼笉閲嶅璁$畻
+                    }
+                }
+            }
+            
+            return taskMileage;
+            
+        } catch (Exception e) {
+            log.error("璁$畻浠诲姟閲岀▼澶辫触锛寁ehicleId={}", vehicleId, e);
+            return BigDecimal.ZERO;
+        }
+    }
+    
+    /**
+     * 鍒ゆ柇涓や釜鏃堕棿娈垫槸鍚︽湁閲嶅彔
+     */
+    private boolean isTimeOverlap(Date start1, Date end1, Date start2, Date end2) {
+        // 鏃堕棿娈�1: [start1, end1]
+        // 鏃堕棿娈�2: [start2, end2]
+        // 鏈夐噸鍙犵殑鏉′欢锛歴tart1 < end2 && start2 < end1
+        return start1.before(end2) && start2.before(end1);
+    }
+
+    /**
+     * 妫�鏌ュ憡璀﹂鐜囬檺鍒�
+     */
+    private boolean checkAlertFrequency(Long vehicleId, AlertConfig config) {
+        try {
+            // 1. 妫�鏌ュ綋鏃ュ憡璀︽鏁�
+            Date today = DateUtils.parseDate(DateUtils.getDate());
+            int dailyCount = alertMapper.selectDailyAlertCount(vehicleId, today);
+            if (dailyCount >= config.dailyLimit) {
+                log.debug("杞﹁締 {} 浠婃棩鍛婅娆℃暟 {} 宸茶揪涓婇檺 {}", vehicleId, dailyCount, config.dailyLimit);
+                return false;
+            }
+            
+            // 2. 妫�鏌ュ憡璀﹂棿闅�
+            Date lastAlertTime = alertMapper.selectLastAlertTime(vehicleId);
+            if (lastAlertTime != null) {
+                long minutesDiff = (new Date().getTime() - lastAlertTime.getTime()) / (1000 * 60);
+                if (minutesDiff < config.alertInterval) {
+                    log.debug("杞﹁締 {} 璺濈涓婃鍛婅浠� {} 鍒嗛挓锛屾湭杈惧埌闂撮殧 {} 鍒嗛挓", 
+                            vehicleId, minutesDiff, config.alertInterval);
+                    return false;
+                }
+            }
+            
+            return true;
+            
+        } catch (Exception e) {
+            log.error("妫�鏌ュ憡璀﹂鐜囧け璐ワ紝vehicleId={}", vehicleId, e);
+            // 鍑洪敊鏃惰皑鎱庡鐞嗭紝鍏佽鍛婅
+            return true;
+        }
+    }
+
+    /**
+     * 鍒涘缓鍛婅骞跺彂閫侀�氱煡
+     */
+    private boolean createAlertAndNotify(VehicleInfo vehicle, BigDecimal mileage, 
+                                         Date startTime, Date endTime, AlertConfig config) {
+        try {
+            Long vehicleId = vehicle.getVehicleId();
+            String vehicleNo = vehicle.getVehicleNo();
+            
+            // 鑾峰彇杞﹁締褰掑睘閮ㄩ棬淇℃伅
+            Long deptId = vehicle.getDeptId();
+            String deptName = vehicle.getDeptName();
+            
+            // 鍒涘缓鍛婅璁板綍
+            boolean created = alertService.checkAndCreateAlert(
+                    vehicleId, vehicleNo, mileage, startTime, endTime, deptId, deptName);
+            
+            if (!created) {
+                return false;
+            }
+            
+            log.info("杞﹁締 {} 浜х敓寮傚父鍛婅锛氭棤浠诲姟杩愯 {}km锛屾椂闂� {} 鑷� {}", 
+                    vehicleNo, mileage,
+                    DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, startTime),
+                    DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, endTime));
+            
+            // 鍙戦�侀�氱煡
+            sendAlertNotification(vehicle, mileage, deptId, config);
+            
+            return true;
+            
+        } catch (Exception e) {
+            log.error("鍒涘缓鍛婅澶辫触锛寁ehicleNo={}", vehicle.getVehicleNo(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 鍙戦�佸憡璀﹂�氱煡
+     */
+    private void sendAlertNotification(VehicleInfo vehicle, BigDecimal mileage, 
+                                       Long deptId, AlertConfig config) {
+        try {
+            // 鑾峰彇閫氱煡鐢ㄦ埛鍒楄〃
+            List<Long> notifyUserIds = getNotifyUsers(deptId, config);
+            if (notifyUserIds.isEmpty()) {
+                log.warn("杞﹁締 {} 鍛婅鏃犻�氱煡鐢ㄦ埛锛岃烦杩囧彂閫�", vehicle.getVehicleNo());
+                return;
+            }
+            
+            // 鏋勯�犻�氱煡鍐呭
+            String title = "杞﹁締寮傚父杩愯鍛婅";
+            String content = String.format("杞﹁締 %s 鍦ㄦ棤浠诲姟鐘舵�佷笅杩愯浜� %.2f 鍏噷锛岃鍙婃椂褰曞叆浠诲姟鍗曪紝鐐瑰嚮鍘诲綍鍗曘��",
+                    vehicle.getVehicleNo(), mileage);
+            
+            // 閫氳繃浼佷笟寰俊鍙戦�侀�氱煡
+            if (qyWechatService != null && qyWechatService.isEnabled()) {
+                for (Long userId : notifyUserIds) {
+                    try {
+                        // 杩欓噷鍙互鏍规嵁瀹為檯闇�姹傛寚瀹氳烦杞摼鎺�
+                        String notifyUrl = "/pagesTask/create-emergency"; // 瀹為檯閾炬帴闇�鏍规嵁涓氬姟璋冩暣
+                        qyWechatService.sendNotifyMessageWithDefaultAppId(userId, title, content, notifyUrl);
+                        log.info("宸插悜鐢ㄦ埛 {} 鍙戦�佽溅杈� {} 寮傚父鍛婅閫氱煡", userId, vehicle.getVehicleNo());
+                    } catch (Exception e) {
+                        log.error("鍚戠敤鎴� {} 鍙戦�佸憡璀﹂�氱煡澶辫触", userId, e);
+                    }
+                }
+            } else {
+                log.warn("浼佷笟寰俊鏈嶅姟鏈惎鐢紝鏃犳硶鍙戦�佸憡璀﹂�氱煡");
+            }
+            
+        } catch (Exception e) {
+            log.error("鍙戦�佸憡璀﹂�氱煡澶辫触锛寁ehicleNo={}", vehicle.getVehicleNo(), e);
+        }
+    }
+
+    /**
+     * 鑾峰彇閫氱煡鐢ㄦ埛鍒楄〃
+     */
+    private List<Long> getNotifyUsers(Long deptId, AlertConfig config) {
+        List<Long> userIds = new ArrayList<>();
+        
+        try {
+            // 1. 浼樺厛浣跨敤閰嶇疆鐨勭敤鎴峰垪琛�
+            if (StringUtils.isNotEmpty(config.notifyUsers)) {
+                String[] userIdStrs = config.notifyUsers.split(",");
+                for (String userIdStr : userIdStrs) {
+                    try {
+                        userIds.add(Long.parseLong(userIdStr.trim()));
+                    } catch (NumberFormatException e) {
+                        log.warn("鏃犳晥鐨勭敤鎴稩D: {}", userIdStr);
+                    }
+                }
+            }
+            
+            // 2. 濡傛灉娌℃湁閰嶇疆鐢ㄦ埛锛屾煡璇㈣溅杈嗘墍灞為儴闂ㄧ殑璐熻矗浜�
+            if ( deptId != null) {
+                SysDept dept = deptService.selectDeptById(deptId);
+                if (dept != null && StringUtils.isNotEmpty(dept.getLeader())) {
+                    // leader鏄敤鎴峰悕锛岄�氳繃鐢ㄦ埛鍚嶆煡璇㈢敤鎴稩D
+                    com.ruoyi.common.core.domain.entity.SysUser leaderUser = 
+                            userService.selectUserByUserName(dept.getLeader());
+                    if (leaderUser != null) {
+                        userIds.add(leaderUser.getUserId());
+                        log.info("浣跨敤閮ㄩ棬 {} 璐熻矗浜�: {} (ID: {})", 
+                                dept.getDeptName(), dept.getLeader(), leaderUser.getUserId());
+                    } else {
+                        log.warn("閮ㄩ棬 {} 璐熻矗浜� {} 鏈壘鍒板搴旂敤鎴�", 
+                                dept.getDeptName(), dept.getLeader());
+                    }
+                }
+            }
+            
+            // 3. 濡傛灉杩樻槸娌℃湁鐢ㄦ埛锛屼娇鐢ㄧ郴缁熼粯璁ら厤缃殑鐢ㄦ埛鍒楄〃锛堟�诲叕鍙歌礋璐d汉锛�
+            if (userIds.isEmpty()) {
+                String defaultUsers = configService.selectConfigByKey("vehicle.alert.default.users");
+                if (StringUtils.isNotEmpty(defaultUsers)) {
+                    String[] defaultUserIds = defaultUsers.split(",");
+                    for (String userId : defaultUserIds) {
+                        try {
+                            userIds.add(Long.parseLong(userId.trim()));
+                            log.info("浣跨敤绯荤粺榛樿閫氱煡鐢ㄦ埛: {}", userId);
+                        } catch (NumberFormatException e) {
+                            log.warn("鏃犳晥鐨勯粯璁ょ敤鎴稩D: {}", userId);
+                        }
+                    }
+                }
+            }
+            
+        } catch (Exception e) {
+            log.error("鑾峰彇閫氱煡鐢ㄦ埛鍒楄〃澶辫触", e);
+        }
+        
+        return userIds;
+    }
+
+    /**
+     * 妫�鏌ュ憡璀﹀姛鑳芥槸鍚﹀惎鐢�
+     */
+    private boolean isAlertEnabled() {
+        try {
+            String enabled = configService.selectConfigByKey("vehicle.alert.enabled");
+            return "true".equalsIgnoreCase(enabled);
+        } catch (Exception e) {
+            log.warn("鑾峰彇鍛婅寮�鍏抽厤缃け璐ワ紝浣跨敤榛樿鍊�(false)", e);
+            return false;
+        }
+    }
+
+    /**
+     * 鑾峰彇杞﹁締鐨勫憡璀﹂厤缃紙浼樺厛绾э細杞﹁締 > 閮ㄩ棬 > 鍏ㄥ眬锛�
+     */
+    private AlertConfig getVehicleAlertConfig(Long vehicleId, Long deptId, AlertConfig globalConfig) {
+        try {
+            // 浠庢暟鎹簱鏌ヨ閰嶇疆
+            VehicleAlertConfig dbConfig = alertConfigService.getConfigByVehicle(vehicleId, deptId);
+            
+            if (dbConfig != null) {
+                // 灏嗘暟鎹簱閰嶇疆杞崲涓篈lertConfig
+                AlertConfig config = new AlertConfig();
+                config.mileageThreshold = dbConfig.getMileageThreshold();
+                config.dailyLimit = dbConfig.getDailyAlertLimit();
+                config.alertInterval = dbConfig.getAlertInterval();
+                config.timeWindow = globalConfig.timeWindow; // 鏃堕棿绐楀彛浣跨敤鍏ㄥ眬閰嶇疆
+                config.notifyUsers = dbConfig.getNotifyUserIds();
+                return config;
+            }
+            
+            // 濡傛灉娌℃湁鏁版嵁搴撻厤缃紝浣跨敤鍏ㄥ眬閰嶇疆
+            return globalConfig;
+            
+        } catch (Exception e) {
+            log.error("鑾峰彇杞﹁締閰嶇疆澶辫触锛寁ehicleId={}", vehicleId, e);
+            return globalConfig;
+        }
+    }
+
+    /**
+     * 鍔犺浇鍛婅閰嶇疆鍙傛暟
+     */
+    private AlertConfig loadAlertConfig() {
+        AlertConfig config = new AlertConfig();
+        
+        try {
+            // 鍏噷鏁伴槇鍊�
+            String thresholdStr = configService.selectConfigByKey("vehicle.alert.mileage.threshold");
+            config.mileageThreshold = StringUtils.isNotEmpty(thresholdStr) 
+                    ? new BigDecimal(thresholdStr) : new BigDecimal("10");
+            
+            // 姣忔棩鍛婅娆℃暟闄愬埗
+            String limitStr = configService.selectConfigByKey("vehicle.alert.daily.limit");
+            config.dailyLimit = StringUtils.isNotEmpty(limitStr) ? Integer.parseInt(limitStr) : 5;
+            
+            // 鍛婅闂撮殧鏃堕棿
+            String intervalStr = configService.selectConfigByKey("vehicle.alert.interval.minutes");
+            config.alertInterval = StringUtils.isNotEmpty(intervalStr) ? Integer.parseInt(intervalStr) : 5;
+            
+            // 鐩戞帶鏃堕棿绐楀彛
+            String windowStr = configService.selectConfigByKey("vehicle.alert.time.window");
+            config.timeWindow = StringUtils.isNotEmpty(windowStr) ? Integer.parseInt(windowStr) : 10;
+            
+            // 閫氱煡鐢ㄦ埛鍒楄〃
+            config.notifyUsers = configService.selectConfigByKey("vehicle.alert.notify.users");
+            
+            log.debug("鍛婅閰嶇疆: 闃堝��={}km, 姣忔棩闄愬埗={}娆�, 闂撮殧={}鍒嗛挓, 鏃堕棿绐楀彛={}鍒嗛挓", 
+                    config.mileageThreshold, config.dailyLimit, config.alertInterval, config.timeWindow);
+            
+        } catch (Exception e) {
+            log.error("鍔犺浇鍛婅閰嶇疆澶辫触锛屼娇鐢ㄩ粯璁ゅ��", e);
+        }
+        
+        return config;
+    }
+
+    /**
+     * 鍛婅閰嶇疆鍐呴儴绫�
+     */
+    private static class AlertConfig {
+        BigDecimal mileageThreshold = new BigDecimal("10");  // 鍏噷鏁伴槇鍊�
+        int dailyLimit = 5;                                   // 姣忔棩鍛婅娆℃暟闄愬埗
+        int alertInterval = 5;                                // 鍛婅闂撮殧锛堝垎閽燂級
+        int timeWindow = 10;                                  // 鐩戞帶鏃堕棿绐楀彛锛堝垎閽燂級
+        String notifyUsers;                                   // 閫氱煡鐢ㄦ埛鍒楄〃
+    }
+}
diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml
index c18868b..8a7b2d1 100644
--- a/ruoyi-system/pom.xml
+++ b/ruoyi-system/pom.xml
@@ -51,9 +51,45 @@
             <artifactId>junit</artifactId>
             <scope>test</scope>
         </dependency>
-        
 
+        <dependency>
+            <groupId>com.baidu.aip</groupId>
+            <artifactId>java-sdk</artifactId>
+            <version>4.16.19</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-simple</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-core</artifactId>
+            <version>4.6.0</version>
+        </dependency>
 
+        <dependency>
+            <groupId>com.tencentcloudapi</groupId>
+            <artifactId>tencentcloud-sdk-java-ocr</artifactId>
+            <version>3.1.1399</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <version>1.15</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>ocr_api20210707</artifactId>
+            <version>3.1.2</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-simple</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
         <!-- Spring Test -->
         <dependency>
             <groupId>org.springframework</groupId>
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/BaiduOCRConfig.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/BaiduOCRConfig.java
new file mode 100644
index 0000000..4a9247c
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/BaiduOCRConfig.java
@@ -0,0 +1,72 @@
+package com.ruoyi.system.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 鐧惧害OCR鏈嶅姟閰嶇疆绫�
+ * 鐢ㄤ簬绠$悊鐧惧害AI寮�鏀惧钩鍙癘CR鏈嶅姟鐨勭浉鍏抽厤缃�
+ */
+@Component
+@ConfigurationProperties(prefix = "baidu.ocr")
+public class BaiduOCRConfig {
+
+    /**
+     * App ID
+     */
+    private String appId;
+
+    /**
+     * API Key
+     */
+    private String apiKey;
+
+    /**
+     * Secret Key
+     */
+    private String secretKey;
+
+    // Getter 鍜� Setter 鏂规硶
+    public String getAppId() {
+        return appId;
+    }
+
+    public void setAppId(String appId) {
+        this.appId = appId;
+    }
+
+    public String getApiKey() {
+        return apiKey;
+    }
+
+    public void setApiKey(String apiKey) {
+        this.apiKey = apiKey;
+    }
+
+    public String getSecretKey() {
+        return secretKey;
+    }
+
+    public void setSecretKey(String secretKey) {
+        this.secretKey = secretKey;
+    }
+
+    /**
+     * 楠岃瘉閰嶇疆鏄惁瀹屾暣
+     * @return 閰嶇疆鏄惁瀹屾暣
+     */
+    public boolean isValid() {
+        return appId != null && !appId.trim().isEmpty() &&
+               apiKey != null && !apiKey.trim().isEmpty() &&
+               secretKey != null && !secretKey.trim().isEmpty();
+    }
+
+    @Override
+    public String toString() {
+        return "BaiduOCRConfig{" +
+                "appId='" + (appId != null ? "***" : "null") + '\'' +
+                ", apiKey='" + (apiKey != null ? "***" : "null") + '\'' +
+                ", secretKey='" + (secretKey != null ? "***" : "null") + '\'' +
+                '}';
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/OCRConfig.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/OCRConfig.java
new file mode 100644
index 0000000..f1a792c
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/OCRConfig.java
@@ -0,0 +1,98 @@
+package com.ruoyi.system.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * OCR鏈嶅姟閰嶇疆绫�
+ * 鐢ㄤ簬绠$悊闃块噷浜慜CR鏈嶅姟鐨勭浉鍏抽厤缃�
+ */
+@Component
+@ConfigurationProperties(prefix = "ali.ocr")
+public class OCRConfig {
+
+    /**
+     * AccessKey ID
+     */
+    private String accessKeyId;
+
+    /**
+     * AccessKey Secret
+     */
+    private String accessKeySecret;
+
+    /**
+     * OCR鏈嶅姟绔偣
+     */
+    private String endpoint = "ocr-api.cn-hangzhou.aliyuncs.com";
+
+    /**
+     * 杩炴帴瓒呮椂鏃堕棿锛堟绉掞級
+     */
+    private Integer connectTimeout = 10000;
+
+    /**
+     * 璇诲彇瓒呮椂鏃堕棿锛堟绉掞級
+     */
+    private Integer readTimeout = 30000;
+
+    // Getter 鍜� Setter 鏂规硶
+    public String getAccessKeyId() {
+        return accessKeyId;
+    }
+
+    public void setAccessKeyId(String accessKeyId) {
+        this.accessKeyId = accessKeyId;
+    }
+
+    public String getAccessKeySecret() {
+        return accessKeySecret;
+    }
+
+    public void setAccessKeySecret(String accessKeySecret) {
+        this.accessKeySecret = accessKeySecret;
+    }
+
+    public String getEndpoint() {
+        return endpoint;
+    }
+
+    public void setEndpoint(String endpoint) {
+        this.endpoint = endpoint;
+    }
+
+    public Integer getConnectTimeout() {
+        return connectTimeout;
+    }
+
+    public void setConnectTimeout(Integer connectTimeout) {
+        this.connectTimeout = connectTimeout;
+    }
+
+    public Integer getReadTimeout() {
+        return readTimeout;
+    }
+
+    public void setReadTimeout(Integer readTimeout) {
+        this.readTimeout = readTimeout;
+    }
+
+    /**
+     * 楠岃瘉閰嶇疆鏄惁瀹屾暣
+     * @return 閰嶇疆鏄惁瀹屾暣
+     */
+    public boolean isValid() {
+        return accessKeyId != null && !accessKeyId.trim().isEmpty() &&
+               accessKeySecret != null && !accessKeySecret.trim().isEmpty();
+    }
+
+    @Override
+    public String toString() {
+        return "OCRConfig{" +
+                "accessKeyId='" + (accessKeyId != null ? "***" : "null") + '\'' +
+                ", endpoint='" + endpoint + '\'' +
+                ", connectTimeout=" + connectTimeout +
+                ", readTimeout=" + readTimeout +
+                '}';
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/TencentOCRConfig.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/TencentOCRConfig.java
new file mode 100644
index 0000000..609dba1
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/TencentOCRConfig.java
@@ -0,0 +1,52 @@
+package com.ruoyi.system.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 鑵捐浜慜CR閰嶇疆绫�
+ * 鐢ㄤ簬閰嶇疆鑵捐浜慜CR鏈嶅姟鐨勭浉鍏冲弬鏁�
+ */
+@Component
+@ConfigurationProperties(prefix = "tencent.ocr")
+public class TencentOCRConfig {
+
+    /**
+     * 鑵捐浜慡ecretId
+     */
+    private String secretId;
+
+    /**
+     * 鑵捐浜慡ecretKey
+     */
+    private String secretKey;
+
+    /**
+     * 鑵捐浜慜CR鏈嶅姟绔偣
+     */
+    private String endpoint = "ocr.tencentcloudapi.com";
+
+    public String getSecretId() {
+        return secretId;
+    }
+
+    public void setSecretId(String secretId) {
+        this.secretId = secretId;
+    }
+
+    public String getSecretKey() {
+        return secretKey;
+    }
+
+    public void setSecretKey(String secretKey) {
+        this.secretKey = secretKey;
+    }
+
+    public String getEndpoint() {
+        return endpoint;
+    }
+
+    public void setEndpoint(String endpoint) {
+        this.endpoint = endpoint;
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/NetworkDiagController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/NetworkDiagController.java
new file mode 100644
index 0000000..97a78c8
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/NetworkDiagController.java
@@ -0,0 +1,160 @@
+package com.ruoyi.system.controller;
+
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.controller.BaseController;
+import org.springframework.web.bind.annotation.*;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.*;
+import java.net.Socket;
+import java.io.IOException;
+
+/**
+ * 缃戠粶璇婃柇Controller
+ * 鎻愪緵OCR鏈嶅姟杩炴帴璇婃柇銆丏NS瑙f瀽娴嬭瘯銆佺綉缁滆繛閫氭�ф祴璇曠瓑鍔熻兘
+ */
+@RestController
+@RequestMapping("/system/diag")
+public class NetworkDiagController extends BaseController {
+
+    /**
+     * 璇婃柇OCR鏈嶅姟杩炴帴
+     * @return 璇婃柇缁撴灉
+     */
+    @GetMapping("/ocrConnection")
+    public AjaxResult diagOcrConnection() {
+        Map<String, Object> result = new HashMap<>();
+        
+        String ocrEndpoint = "ocr-api.cn-hangzhou.aliyuncs.com";
+        int port = 443; // HTTPS绔彛
+        
+        try {
+            result.put("dns", testDns(ocrEndpoint));
+            result.put("connection", testConnection(ocrEndpoint, port));
+            result.put("network", getNetworkConfig());
+            
+            return success(result);
+            
+        } catch (Exception e) {
+            logger.error("缃戠粶璇婃柇澶辫触", e);
+            return error("缃戠粶璇婃柇澶辫触: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 娴嬭瘯DNS瑙f瀽
+     * @param params 鍖呭惈hostname鐨勫弬鏁�
+     * @return DNS瑙f瀽缁撴灉
+     */
+    @PostMapping("/testDns")
+    public AjaxResult testDns(@RequestBody Map<String, String> params) {
+        String hostname = params.get("hostname");
+        if (hostname == null || hostname.trim().isEmpty()) {
+            return error("涓绘満鍚嶄笉鑳戒负绌�");
+        }
+        return success(testDns(hostname));
+    }
+
+    /**
+     * 娴嬭瘯缃戠粶杩為�氭��
+     * @param params 鍖呭惈host鍜宲ort鐨勫弬鏁�
+     * @return 杩為�氭�ф祴璇曠粨鏋�
+     */
+    @PostMapping("/testConnectivity")
+    public AjaxResult testConnectivity(@RequestBody Map<String, Object> params) {
+        String host = (String) params.get("host");
+        Integer port = (Integer) params.get("port");
+        
+        if (host == null || host.trim().isEmpty()) {
+            return error("涓绘満涓嶈兘涓虹┖");
+        }
+        if (port == null) {
+            return error("绔彛涓嶈兘涓虹┖");
+        }
+        
+        return success(testConnection(host, port));
+    }
+
+    /**
+     * 娴嬭瘯DNS瑙f瀽
+     * @param hostname 涓绘満鍚�
+     * @return DNS瑙f瀽缁撴灉
+     */
+    private Map<String, Object> testDns(String hostname) {
+        Map<String, Object> dnsResult = new HashMap<>();
+        
+        try {
+            InetAddress[] addresses = InetAddress.getAllByName(hostname);
+            dnsResult.put("success", true);
+            dnsResult.put("hostname", hostname);
+            dnsResult.put("ipCount", addresses.length);
+            
+            String[] ips = new String[addresses.length];
+            for (int i = 0; i < addresses.length; i++) {
+                ips[i] = addresses[i].getHostAddress();
+            }
+            dnsResult.put("ips", ips);
+            
+        } catch (UnknownHostException e) {
+            dnsResult.put("success", false);
+            dnsResult.put("error", "DNS瑙f瀽澶辫触: " + e.getMessage());
+            dnsResult.put("suggestion", "璇锋鏌NS鏈嶅姟鍣ㄩ厤缃紝鍙互灏濊瘯浣跨敤鍏叡DNS锛堝8.8.8.8锛�");
+        }
+        
+        return dnsResult;
+    }
+
+    /**
+     * 娴嬭瘯缃戠粶杩炴帴
+     * @param hostname 涓绘満鍚�
+     * @param port 绔彛鍙�
+     * @return 杩炴帴娴嬭瘯缁撴灉
+     */
+    private Map<String, Object> testConnection(String hostname, int port) {
+        Map<String, Object> connResult = new HashMap<>();
+        long startTime = System.currentTimeMillis();
+        
+        try (Socket socket = new Socket()) {
+            socket.connect(new java.net.InetSocketAddress(hostname, port), 10000); // 10绉掕秴鏃�
+            long endTime = System.currentTimeMillis();
+            long responseTime = endTime - startTime;
+            
+            connResult.put("success", true);
+            connResult.put("hostname", hostname);
+            connResult.put("port", port);
+            connResult.put("responseTime", responseTime + "ms");
+            connResult.put("connected", true);
+            
+        } catch (IOException e) {
+            long endTime = System.currentTimeMillis();
+            long responseTime = endTime - startTime;
+            
+            connResult.put("success", false);
+            connResult.put("hostname", hostname);
+            connResult.put("port", port);
+            connResult.put("responseTime", responseTime + "ms");
+            connResult.put("error", "杩炴帴澶辫触: " + e.getMessage());
+            connResult.put("suggestion", "璇锋鏌ラ槻鐏璁剧疆锛岀‘璁ょ鍙�" + port + "鏄惁寮�鏀�");
+        }
+        
+        return connResult;
+    }
+
+    /**
+     * 鑾峰彇缃戠粶閰嶇疆淇℃伅
+     * @return 缃戠粶閰嶇疆淇℃伅
+     */
+    private Map<String, Object> getNetworkConfig() {
+        Map<String, Object> networkConfig = new HashMap<>();
+        try {
+            networkConfig.put("httpProxy", System.getProperty("http.proxyHost"));
+            networkConfig.put("httpsProxy", System.getProperty("https.proxyHost"));
+            networkConfig.put("localHostAddress", InetAddress.getLocalHost().getHostAddress());
+            networkConfig.put("localHostName", InetAddress.getLocalHost().getHostName());
+        } catch (Exception e) {
+            networkConfig.put("error", "鑾峰彇缃戠粶閰嶇疆澶辫触: " + e.getMessage());
+        }
+        return networkConfig;
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..d370b89
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/OCRController.java
@@ -0,0 +1,433 @@
+package com.ruoyi.system.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.utils.file.FileUploadUtils;
+import com.ruoyi.system.utils.AliOCRUtil;
+import com.ruoyi.system.utils.BaiduOCRUtil;
+import com.ruoyi.system.utils.TencentOCRUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * OCR璇嗗埆Controller
+ * 鏀寔闃块噷浜慜CR鍜岀櫨搴CR鏈嶅姟
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/system/ocr")
+public class OCRController extends BaseController {
+
+    @Autowired
+    private AliOCRUtil aliOCRUtil;
+
+    // 鏀寔鐨凮CR璇嗗埆绫诲瀷
+    private static final List<String> SUPPORTED_TYPES = Arrays.asList("General", "Invoice", "IdCard", "HandWriting");
+
+    /**
+     * 涓婁紶鍥剧墖骞惰繘琛孫CR璇嗗埆
+     * @param file 涓婁紶鐨勫浘鐗囨枃浠�
+     * @param type 璇嗗埆绫诲瀷锛圙eneral-閫氱敤, Invoice-鍙戠エ, IdCard-韬唤璇�, HandWriting-鎵嬪啓浣擄級
+     * @param provider OCR鏈嶅姟鎻愪緵鍟嗭紙ali-闃块噷浜�, baidu-鐧惧害锛�
+     * @return OCR璇嗗埆缁撴灉
+     */
+    @PostMapping(value = "/recognize", consumes = "multipart/form-data")
+    public AjaxResult recognizeImage(@RequestParam("file") MultipartFile file,
+                                      @RequestParam(value = "type", defaultValue = "General") String type,
+                                      @RequestParam(value = "provider", defaultValue = "ali") String provider,
+                                      @RequestParam(value = "itemNames", required = false) String[] itemNames) {
+        try {
+            if (file.isEmpty()) {
+                return error("涓婁紶鍥剧墖涓嶈兘涓虹┖");
+            }
+
+            // 楠岃瘉璇嗗埆绫诲瀷
+            if (!SUPPORTED_TYPES.contains(type)) {
+                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);
+
+            // 鏍规嵁鎻愪緵鍟嗚皟鐢ㄤ笉鍚岀殑OCR鏈嶅姟
+            JSONObject ocrResult;
+            if ("baidu".equalsIgnoreCase(provider)) {
+                // 鐧惧害OCR鍙敮鎸侀儴鍒嗙被鍨�
+                if ("General".equals(type)) {
+                    ocrResult = BaiduOCRUtil.generalRecognize(tempFile);
+                } else if ("HandWriting".equals(type)) {
+                    ocrResult = BaiduOCRUtil.handwritingRecognize(tempFile);
+                } else {
+                    ocrResult = BaiduOCRUtil.generalRecognize(tempFile); // 榛樿浣跨敤閫氱敤璇嗗埆
+                }
+            } else if ("tencent".equalsIgnoreCase(provider)) {
+                // 鑵捐浜慜CR鍙敮鎸侀儴鍒嗙被鍨�
+                if ("General".equals(type)) {
+                    ocrResult = TencentOCRUtil.generalRecognize(tempFile);
+                } else if ("HandWriting".equals(type)) {
+                    ocrResult = TencentOCRUtil.handwritingRecognize(tempFile.getAbsolutePath(), itemNames);
+                } else {
+                    ocrResult = TencentOCRUtil.generalRecognize(tempFile); // 榛樿浣跨敤閫氱敤璇嗗埆
+                }
+            } else {
+                // 闃块噷浜慜CR
+                ocrResult = AliOCRUtil.recognizeTextByFile(tempFile, type);
+            }
+
+            // 鍒犻櫎涓存椂鏂囦欢
+            tempFile.delete();
+
+            // 鏋勫缓杩斿洖缁撴灉
+            Map<String, Object> result = new HashMap<>();
+            result.put("ocrResult", ocrResult);
+            result.put("fileName", originalFilename);
+            result.put("type", type);
+            result.put("provider", provider);
+
+            if (ocrResult.getBooleanValue("success")) {
+                return success(result);
+            } else {
+                return error("OCR璇嗗埆澶辫触: " + ocrResult.getString("error"));
+            }
+
+        } catch (Exception e) {
+            logger.error("OCR璇嗗埆寮傚父", e);
+            return error("OCR璇嗗埆寮傚父: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 閫氳繃鍥剧墖URL杩涜OCR璇嗗埆
+     * @param imageUrl 鍥剧墖URL鍦板潃
+     * @param type 璇嗗埆绫诲瀷锛圙eneral-閫氱敤, Invoice-鍙戠エ, IdCard-韬唤璇�, HandWriting-鎵嬪啓浣擄級
+     * @param provider OCR鏈嶅姟鎻愪緵鍟嗭紙ali-闃块噷浜�, baidu-鐧惧害锛�
+     * @return OCR璇嗗埆缁撴灉
+     */
+    @GetMapping("/recognizeByUrl")
+    public AjaxResult recognizeByUrl(@RequestParam("imageUrl") String imageUrl,
+                                      @RequestParam(value = "type", defaultValue = "General") String type,
+                                      @RequestParam(value = "provider", defaultValue = "ali") String provider,
+                                      @RequestParam(value = "itemNames", required = false) String[] itemNames) {
+        try {
+            // 楠岃瘉璇嗗埆绫诲瀷
+            if (!SUPPORTED_TYPES.contains(type)) {
+                return error("涓嶆敮鎸佺殑璇嗗埆绫诲瀷: " + type + ", 鏀寔鐨勭被鍨�: " + String.join(",", SUPPORTED_TYPES));
+            }
+
+            // 鏍规嵁鎻愪緵鍟嗚皟鐢ㄤ笉鍚岀殑OCR鏈嶅姟
+            JSONObject ocrResult;
+            if ("baidu".equalsIgnoreCase(provider)) {
+                // 鐧惧害OCR鍙敮鎸侀儴鍒嗙被鍨�
+                if ("General".equals(type)) {
+                    ocrResult = BaiduOCRUtil.generalRecognize(imageUrl);
+                } else if ("HandWriting".equals(type)) {
+                    ocrResult = BaiduOCRUtil.handwritingRecognize(imageUrl);
+                } else {
+                    ocrResult = BaiduOCRUtil.generalRecognize(imageUrl); // 榛樿浣跨敤閫氱敤璇嗗埆
+                }
+            } else if ("tencent".equalsIgnoreCase(provider)) {
+                // 鑵捐浜慜CR鍙敮鎸侀儴鍒嗙被鍨�
+                if ("General".equals(type)) {
+                    ocrResult = TencentOCRUtil.generalRecognize(imageUrl);
+                } else if ("HandWriting".equals(type)) {
+                    ocrResult = TencentOCRUtil.handwritingRecognize(imageUrl, itemNames);
+                } else {
+                    ocrResult = TencentOCRUtil.generalRecognize(imageUrl); // 榛樿浣跨敤閫氱敤璇嗗埆
+                }
+            } else {
+                // 闃块噷浜慜CR
+                ocrResult = AliOCRUtil.recognizeTextByUrl(imageUrl, type);
+            }
+
+            // 鏋勫缓杩斿洖缁撴灉
+            Map<String, Object> result = new HashMap<>();
+            result.put("ocrResult", ocrResult);
+            result.put("imageUrl", imageUrl);
+            result.put("type", type);
+            result.put("provider", provider);
+
+            if (ocrResult.getBooleanValue("success")) {
+                return success(result);
+            } else {
+                return error("OCR璇嗗埆澶辫触: " + ocrResult.getString("error"));
+            }
+
+        } catch (Exception e) {
+            logger.error("OCR璇嗗埆寮傚父", e);
+            return error("OCR璇嗗埆寮傚父: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 鑾峰彇鏀寔鐨凮CR璇嗗埆绫诲瀷鍒楄〃
+     * @return 璇嗗埆绫诲瀷鍒楄〃
+     */
+    @GetMapping("/types")
+    public AjaxResult getSupportedTypes() {
+        Map<String, Object> result = new HashMap<>();
+        result.put("types", SUPPORTED_TYPES);
+        
+        List<Map<String, String>> typeList = SUPPORTED_TYPES.stream().map(type -> {
+            Map<String, String> typeInfo = new HashMap<>();
+            typeInfo.put("value", type);
+            
+            // 鏍规嵁绫诲瀷璁剧疆鏄剧ず鍚嶇О
+            switch (type) {
+                case "General":
+                    typeInfo.put("label", "閫氱敤鏂囧瓧璇嗗埆");
+                    break;
+                case "Invoice":
+                    typeInfo.put("label", "鍙戠エ璇嗗埆");
+                    break;
+                case "IdCard":
+                    typeInfo.put("label", "韬唤璇佽瘑鍒�");
+                    break;
+                case "HandWriting":
+                    typeInfo.put("label", "鎵嬪啓浣撹瘑鍒�");
+                    break;
+                default:
+                    typeInfo.put("label", type);
+                    break;
+            }
+            return typeInfo;
+        }).collect(Collectors.toList());
+        
+        result.put("typeList", typeList);
+        return success(result);
+    }
+
+    /**
+     * 鑾峰彇鏀寔鐨凮CR鏈嶅姟鎻愪緵鍟嗗垪琛�
+     * @return OCR鏈嶅姟鎻愪緵鍟嗗垪琛�
+     */
+    @GetMapping("/providers")
+    public AjaxResult getSupportedProviders() {
+        Map<String, Object> result = new HashMap<>();
+        List<Map<String, String>> providerList = Arrays.asList(
+            createProviderInfo("ali", "闃块噷浜慜CR", true),
+            createProviderInfo("baidu", "鐧惧害OCR", true),
+            createProviderInfo("tencent", "鑵捐浜慜CR", true)
+        );
+        result.put("providers", providerList);
+        return success(result);
+    }
+
+    /**
+     * 鎻愬彇OCR缁撴灉涓殑鐩爣瀛楁
+     * @param ocrResult OCR鍘熷缁撴灉
+     * @return 鎻愬彇鐨勫瓧娈典俊鎭�
+     */
+    @PostMapping("/extractFields")
+    public AjaxResult extractFields(@RequestBody JSONObject ocrResult) {
+        try {
+            // 妫�鏌ユ槸鍚︿负鐧惧害OCR缁撴灉
+            String provider = ocrResult.getString("provider");
+            Map<String, String> extracted;
+            if ("baidu".equalsIgnoreCase(provider)) {
+                extracted = BaiduOCRUtil.extractTargetFields(ocrResult);
+            } else if ("tencent".equalsIgnoreCase(provider)) {
+                extracted = TencentOCRUtil.extractTargetFields(ocrResult);
+            } else {
+                extracted = AliOCRUtil.extractTargetFields(ocrResult);
+            }
+            return success(extracted);
+        } catch (Exception e) {
+            logger.error("瀛楁鎻愬彇寮傚父", e);
+            return error("瀛楁鎻愬彇寮傚父: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 鑵捐浜戞墜鍐欎綋璇嗗埆锛堟敮鎸佽嚜瀹氫箟瀛楁鎻愬彇锛�
+     * @param file 涓婁紶鐨勫浘鐗囨枃浠�
+     * @param itemNames 闇�瑕佹彁鍙栫殑瀛楁鍚嶇О鏁扮粍
+     * @return 璇嗗埆缁撴灉 Map锛宬ey涓哄瓧娈靛悕锛寁alue涓鸿瘑鍒唴瀹�
+     */
+    @PostMapping(value = "/tencent/handwriting", consumes = "multipart/form-data")
+    public AjaxResult tencentHandwritingRecognize(@RequestParam("file") MultipartFile file,
+                                                   @RequestParam(value = "itemNames", required = false) String[] itemNames) {
+        try {
+            if (file.isEmpty()) {
+                return error("涓婁紶鍥剧墖涓嶈兘涓虹┖");
+            }
+
+            // 淇濆瓨涓存椂鏂囦欢
+            String tempDir = System.getProperty("java.io.tmpdir");
+            String originalFilename = file.getOriginalFilename();
+            File tempFile = new File(tempDir, System.currentTimeMillis() + "_" + originalFilename);
+            file.transferTo(tempFile);
+
+            // 璋冪敤鑵捐浜戞墜鍐欎綋璇嗗埆
+            Map<String, String> resultMap = TencentOCRUtil.handwritingRecognizeWith(tempFile.getAbsolutePath(), itemNames);
+
+            // 鍒犻櫎涓存椂鏂囦欢
+            tempFile.delete();
+
+            // 妫�鏌ユ槸鍚︽湁閿欒
+            if (resultMap.containsKey("error")) {
+                return error("鑵捐浜慜CR鎵嬪啓浣撹瘑鍒け璐�: " + resultMap.get("error"));
+            }
+
+            // 鏋勫缓杩斿洖缁撴灉
+            Map<String, Object> result = new HashMap<>();
+            result.put("fileName", originalFilename);
+            result.put("type", "HandWriting");
+            result.put("provider", "tencent");
+            result.put("fields", resultMap);
+            result.put("fieldCount", resultMap.size());
+
+            return success(result);
+
+        } catch (Exception e) {
+            logger.error("鑵捐浜慜CR鎵嬪啓浣撹瘑鍒紓甯�", e);
+            return error("鑵捐浜慜CR鎵嬪啓浣撹瘑鍒紓甯�: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 鑵捐浜戞墜鍐欎綋璇嗗埆閫氳繃URL锛堟敮鎸佽嚜瀹氫箟瀛楁鎻愬彇锛�
+     * @param imageUrl 鍥剧墖URL鍦板潃
+     * @param itemNames 闇�瑕佹彁鍙栫殑瀛楁鍚嶇О鏁扮粍
+     * @return 璇嗗埆缁撴灉 Map锛宬ey涓哄瓧娈靛悕锛寁alue涓鸿瘑鍒唴瀹�
+     */
+    @GetMapping("/tencent/handwritingByUrl")
+    public AjaxResult tencentHandwritingRecognizeByUrl(@RequestParam("imageUrl") String imageUrl,
+                                                        @RequestParam(value = "itemNames", required = false) String[] itemNames) {
+        try {
+            // 璋冪敤鑵捐浜戞墜鍐欎綋璇嗗埆
+            Map<String, String> resultMap = TencentOCRUtil.handwritingRecognizeWith(imageUrl, itemNames);
+
+            // 妫�鏌ユ槸鍚︽湁閿欒
+            if (resultMap.containsKey("error")) {
+                return error("鑵捐浜慜CR鎵嬪啓浣撹瘑鍒け璐�: " + resultMap.get("error"));
+            }
+
+            // 鏋勫缓杩斿洖缁撴灉
+            Map<String, Object> result = new HashMap<>();
+            result.put("imageUrl", imageUrl);
+            result.put("type", "HandWriting");
+            result.put("provider", "tencent");
+            result.put("fields", resultMap);
+            result.put("fieldCount", resultMap.size());
+
+            return success(result);
+
+        } catch (Exception e) {
+            logger.error("鑵捐浜慜CR鎵嬪啓浣撹瘑鍒紓甯�", e);
+            return error("鑵捐浜慜CR鎵嬪啓浣撹瘑鍒紓甯�: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 鑵捐浜戞墜鍐欎綋璇嗗埆锛堟敮鎸佸鍥剧墖鎵归噺璇嗗埆锛�
+     * @param files 涓婁紶鐨勫浘鐗囨枃浠舵暟缁�
+     * @param itemNames 闇�瑕佹彁鍙栫殑瀛楁鍚嶇О鏁扮粍
+     * @return 璇嗗埆缁撴灉锛屽悎骞舵墍鏈夊浘鐗囩殑璇嗗埆瀛楁
+     */
+    @PostMapping(value = "/tencent/handwriting/batch", consumes = "multipart/form-data")
+    public AjaxResult tencentHandwritingRecognizeBatch(@RequestParam("files") MultipartFile[] files,
+                                                        @RequestParam(value = "itemNames", required = false) String[] itemNames) {
+        try {
+            if (files == null || files.length == 0) {
+                return error("涓婁紶鍥剧墖涓嶈兘涓虹┖");
+            }
+
+            // 鍚堝苟鎵�鏈夊浘鐗囩殑璇嗗埆缁撴灉
+            Map<String, String> mergedResultMap = new HashMap<>();
+            int successCount = 0;
+            int failCount = 0;
+            StringBuilder errorMessages = new StringBuilder();
+
+            for (MultipartFile file : files) {
+                if (file.isEmpty()) {
+                    continue;
+                }
+
+                try {
+                    // 淇濆瓨涓存椂鏂囦欢
+                    String tempDir = System.getProperty("java.io.tmpdir");
+                    String originalFilename = file.getOriginalFilename();
+                    File tempFile = new File(tempDir, System.currentTimeMillis() + "_" + originalFilename);
+                    file.transferTo(tempFile);
+
+                    // 璋冪敤鑵捐浜戞墜鍐欎綋璇嗗埆
+                    Map<String, String> resultMap = TencentOCRUtil.handwritingRecognizeWith(tempFile.getAbsolutePath(), itemNames);
+
+                    // 鍒犻櫎涓存椂鏂囦欢
+                    tempFile.delete();
+
+                    // 妫�鏌ユ槸鍚︽湁閿欒
+                    if (resultMap.containsKey("error")) {
+                        failCount++;
+                        errorMessages.append(originalFilename).append(":").append(resultMap.get("error")).append("; ");
+                        logger.warn("鍥剧墖 {} 璇嗗埆澶辫触: {}", originalFilename, resultMap.get("error"));
+                    } else {
+                        // 鍚堝苟璇嗗埆缁撴灉锛堝鏋渒ey宸插瓨鍦紝涓嶈鐩栵級
+                        for (Map.Entry<String, String> entry : resultMap.entrySet()) {
+                            if (!mergedResultMap.containsKey(entry.getKey()) || mergedResultMap.get(entry.getKey()).isEmpty()) {
+                                mergedResultMap.put(entry.getKey(), entry.getValue());
+                            }
+                        }
+                        successCount++;
+                        logger.info("鍥剧墖 {} 璇嗗埆鎴愬姛锛屾彁鍙� {} 涓瓧娈�", originalFilename, resultMap.size());
+                    }
+                } catch (Exception e) {
+                    failCount++;
+                    errorMessages.append(file.getOriginalFilename()).append(":").append(e.getMessage()).append("; ");
+                    logger.error("澶勭悊鍥剧墖 {} 鏃跺彂鐢熷紓甯�", file.getOriginalFilename(), e);
+                }
+            }
+
+            // 鏋勫缓杩斿洖缁撴灉
+            Map<String, Object> result = new HashMap<>();
+            result.put("type", "HandWriting");
+            result.put("provider", "tencent");
+            result.put("fields", mergedResultMap);
+            result.put("fieldCount", mergedResultMap.size());
+            result.put("totalImages", files.length);
+            result.put("successCount", successCount);
+            result.put("failCount", failCount);
+            
+            if (failCount > 0) {
+                result.put("errors", errorMessages.toString());
+            }
+
+            if (successCount == 0) {
+                return error("鎵�鏈夊浘鐗囪瘑鍒け璐�: " + errorMessages.toString());
+            }
+
+            return success(result);
+
+        } catch (Exception e) {
+            logger.error("鑵捐浜慜CR鎵嬪啓浣撴壒閲忚瘑鍒紓甯�", e);
+            return error("鑵捐浜慜CR鎵嬪啓浣撴壒閲忚瘑鍒紓甯�: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 鍒涘缓鏈嶅姟鎻愪緵鍟嗕俊鎭�
+     * @param value 鏈嶅姟鎻愪緵鍟嗘爣璇�
+     * @param label 鏈嶅姟鎻愪緵鍟嗘樉绀哄悕绉�
+     * @param available 鏄惁鍙敤
+     * @return 鏈嶅姟鎻愪緵鍟嗕俊鎭�
+     */
+    private Map<String, String> createProviderInfo(String value, String label, boolean available) {
+        Map<String, String> providerInfo = new HashMap<>();
+        providerInfo.put("value", value);
+        providerInfo.put("label", label);
+        providerInfo.put("available", String.valueOf(available));
+        return providerInfo;
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/HospitalTokenizerTask.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/HospitalTokenizerTask.java
new file mode 100644
index 0000000..95d6b4d
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/HospitalTokenizerTask.java
@@ -0,0 +1,143 @@
+package com.ruoyi.system.domain;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 鍖婚櫌鍒嗚瘝浠诲姟鐘舵��
+ * 
+ * @author ruoyi
+ * @date 2026-01-20
+ */
+public class HospitalTokenizerTask implements Serializable {
+    
+    private static final long serialVersionUID = 1L;
+    
+    /** 浠诲姟ID */
+    private String taskId;
+    
+    /** 浠诲姟鐘舵��: RUNNING-杩愯涓�, SUCCESS-鎴愬姛, FAILED-澶辫触 */
+    private String status;
+    
+    /** 鎬诲尰闄㈡暟閲� */
+    private Integer totalCount;
+    
+    /** 宸插鐞嗘暟閲� */
+    private Integer processedCount;
+    
+    /** 鎴愬姛鏁伴噺 */
+    private Integer successCount;
+    
+    /** 澶辫触鏁伴噺 */
+    private Integer failedCount;
+    
+    /** 杩涘害鐧惧垎姣� */
+    private Integer progress;
+    
+    /** 寮�濮嬫椂闂� */
+    private Date startTime;
+    
+    /** 缁撴潫鏃堕棿 */
+    private Date endTime;
+    
+    /** 閿欒淇℃伅 */
+    private String errorMessage;
+    
+    public HospitalTokenizerTask() {
+    }
+    
+    public HospitalTokenizerTask(String taskId) {
+        this.taskId = taskId;
+        this.status = "RUNNING";
+        this.totalCount = 0;
+        this.processedCount = 0;
+        this.successCount = 0;
+        this.failedCount = 0;
+        this.progress = 0;
+        this.startTime = new Date();
+    }
+    
+    public String getTaskId() {
+        return taskId;
+    }
+    
+    public void setTaskId(String taskId) {
+        this.taskId = taskId;
+    }
+    
+    public String getStatus() {
+        return status;
+    }
+    
+    public void setStatus(String status) {
+        this.status = status;
+    }
+    
+    public Integer getTotalCount() {
+        return totalCount;
+    }
+    
+    public void setTotalCount(Integer totalCount) {
+        this.totalCount = totalCount;
+    }
+    
+    public Integer getProcessedCount() {
+        return processedCount;
+    }
+    
+    public void setProcessedCount(Integer processedCount) {
+        this.processedCount = processedCount;
+        // 鑷姩璁$畻杩涘害
+        if (totalCount != null && totalCount > 0) {
+            this.progress = (int) ((processedCount * 100.0) / totalCount);
+        }
+    }
+    
+    public Integer getSuccessCount() {
+        return successCount;
+    }
+    
+    public void setSuccessCount(Integer successCount) {
+        this.successCount = successCount;
+    }
+    
+    public Integer getFailedCount() {
+        return failedCount;
+    }
+    
+    public void setFailedCount(Integer failedCount) {
+        this.failedCount = failedCount;
+    }
+    
+    public Integer getProgress() {
+        return progress;
+    }
+    
+    public void setProgress(Integer progress) {
+        this.progress = progress;
+    }
+    
+    public Date getStartTime() {
+        return startTime;
+    }
+    
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+    
+    public Date getEndTime() {
+        return endTime;
+    }
+    
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+    
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+    
+    public void setErrorMessage(String errorMessage) {
+        this.errorMessage = errorMessage;
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TbHospData.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/TbHospData.java
index 4c11f45..9c692ff 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TbHospData.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/TbHospData.java
@@ -65,6 +65,9 @@
     /** 鍖婚櫌绾у埆 */
     private Integer hospLevel;
 
+    /** 鍖婚櫌淇℃伅鍒嗚瘝锛堥�楀彿鍒嗛殧锛� */
+    private String hospKeywords;
+
     /** 鏁版嵁鐘舵�侊紙0姝e父 1鍋滅敤锛� */
     private String status;
 
@@ -196,6 +199,14 @@
         this.hospLevel = hospLevel;
     }
 
+    public String getHospKeywords() {
+        return hospKeywords;
+    }
+
+    public void setHospKeywords(String hospKeywords) {
+        this.hospKeywords = hospKeywords;
+    }
+
     public String getStatus() {
         return status;
     }
@@ -223,6 +234,7 @@
             .append("hospIntroducerId", getHospIntroducerId())
             .append("hospIntroducerDate", getHospIntroducerDate())
             .append("hospLevel", getHospLevel())
+            .append("hospKeywords", getHospKeywords())
             .append("status", getStatus())
             .append("remark", getRemark())
             .append("createBy", getCreateBy())
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleAbnormalAlert.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleAbnormalAlert.java
new file mode 100644
index 0000000..a9a65f0
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleAbnormalAlert.java
@@ -0,0 +1,302 @@
+package com.ruoyi.system.domain;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 杞﹁締寮傚父杩愯鍛婅璁板綍瀵硅薄 tb_vehicle_abnormal_alert
+ * 
+ * @author ruoyi
+ */
+public class VehicleAbnormalAlert extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /** 鍛婅ID */
+    private Long alertId;
+
+    /** 杞﹁締ID */
+    @Excel(name = "杞﹁締ID")
+    private Long vehicleId;
+
+    /** 杞︾墝鍙� */
+    @Excel(name = "杞︾墝鍙�")
+    private String vehicleNo;
+
+    /** 鍛婅鏃ユ湡 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "鍛婅鏃ユ湡", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date alertDate;
+
+    /** 鍛婅鏃堕棿 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "鍛婅鏃堕棿", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date alertTime;
+
+    /** 绱杩愯鍏噷鏁�(鍏噷) */
+    @Excel(name = "绱鍏噷鏁�")
+    private BigDecimal mileage;
+
+    /** 鍛婅绫诲瀷(NO_TASK_MILEAGE-鏃犱换鍔¤秴鍏噷) */
+    @Excel(name = "鍛婅绫诲瀷", readConverterExp = "NO_TASK_MILEAGE=鏃犱换鍔¤秴鍏噷")
+    private String alertType;
+
+    /** 鍛婅鍘熷洜鎻忚堪 */
+    @Excel(name = "鍛婅鍘熷洜")
+    private String alertReason;
+
+    /** 寮�濮嬭繍琛屾椂闂� */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "寮�濮嬫椂闂�", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date startTime;
+
+    /** 缁撴潫杩愯鏃堕棿 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "缁撴潫鏃堕棿", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date endTime;
+
+    /** 褰撴棩鍛婅娆℃暟 */
+    @Excel(name = "褰撴棩鍛婅娆℃暟")
+    private Integer alertCount;
+
+    /** 鐘舵�侊紙0-鏈鐞� 1-宸插鐞� 2-宸插拷鐣ワ級 */
+    @Excel(name = "鐘舵��", readConverterExp = "0=鏈鐞�,1=宸插鐞�,2=宸插拷鐣�")
+    private String status;
+
+    /** 澶勭悊浜篒D */
+    private Long handlerId;
+
+    /** 澶勭悊浜哄鍚� */
+    @Excel(name = "澶勭悊浜�")
+    private String handlerName;
+
+    /** 澶勭悊鏃堕棿 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "澶勭悊鏃堕棿", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date handleTime;
+
+    /** 澶勭悊澶囨敞 */
+    @Excel(name = "澶勭悊澶囨敞")
+    private String handleRemark;
+
+    /** 閫氱煡鐘舵�侊紙0-鏈彂閫� 1-宸插彂閫� 2-鍙戦�佸け璐ワ級 */
+    @Excel(name = "閫氱煡鐘舵��", readConverterExp = "0=鏈彂閫�,1=宸插彂閫�,2=鍙戦�佸け璐�")
+    private String notifyStatus;
+
+    /** 閫氱煡鏃堕棿 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date notifyTime;
+
+    /** 閫氱煡鐢ㄦ埛ID鍒楄〃锛堥�楀彿鍒嗛殧锛� */
+    private String notifyUsers;
+
+    /** 褰掑睘閮ㄩ棬ID */
+    @Excel(name = "褰掑睘閮ㄩ棬ID")
+    private Long deptId;
+
+    /** 褰掑睘閮ㄩ棬鍚嶇О */
+    @Excel(name = "褰掑睘閮ㄩ棬")
+    private String deptName;
+
+    public void setAlertId(Long alertId) {
+        this.alertId = alertId;
+    }
+
+    public Long getAlertId() {
+        return alertId;
+    }
+
+    public void setVehicleId(Long vehicleId) {
+        this.vehicleId = vehicleId;
+    }
+
+    public Long getVehicleId() {
+        return vehicleId;
+    }
+
+    public void setVehicleNo(String vehicleNo) {
+        this.vehicleNo = vehicleNo;
+    }
+
+    public String getVehicleNo() {
+        return vehicleNo;
+    }
+
+    public void setAlertDate(Date alertDate) {
+        this.alertDate = alertDate;
+    }
+
+    public Date getAlertDate() {
+        return alertDate;
+    }
+
+    public void setAlertTime(Date alertTime) {
+        this.alertTime = alertTime;
+    }
+
+    public Date getAlertTime() {
+        return alertTime;
+    }
+
+    public void setMileage(BigDecimal mileage) {
+        this.mileage = mileage;
+    }
+
+    public BigDecimal getMileage() {
+        return mileage;
+    }
+
+    public void setAlertType(String alertType) {
+        this.alertType = alertType;
+    }
+
+    public String getAlertType() {
+        return alertType;
+    }
+
+    public void setAlertReason(String alertReason) {
+        this.alertReason = alertReason;
+    }
+
+    public String getAlertReason() {
+        return alertReason;
+    }
+
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setAlertCount(Integer alertCount) {
+        this.alertCount = alertCount;
+    }
+
+    public Integer getAlertCount() {
+        return alertCount;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setHandlerId(Long handlerId) {
+        this.handlerId = handlerId;
+    }
+
+    public Long getHandlerId() {
+        return handlerId;
+    }
+
+    public void setHandlerName(String handlerName) {
+        this.handlerName = handlerName;
+    }
+
+    public String getHandlerName() {
+        return handlerName;
+    }
+
+    public void setHandleTime(Date handleTime) {
+        this.handleTime = handleTime;
+    }
+
+    public Date getHandleTime() {
+        return handleTime;
+    }
+
+    public void setHandleRemark(String handleRemark) {
+        this.handleRemark = handleRemark;
+    }
+
+    public String getHandleRemark() {
+        return handleRemark;
+    }
+
+    public void setNotifyStatus(String notifyStatus) {
+        this.notifyStatus = notifyStatus;
+    }
+
+    public String getNotifyStatus() {
+        return notifyStatus;
+    }
+
+    public void setNotifyTime(Date notifyTime) {
+        this.notifyTime = notifyTime;
+    }
+
+    public Date getNotifyTime() {
+        return notifyTime;
+    }
+
+    public void setNotifyUsers(String notifyUsers) {
+        this.notifyUsers = notifyUsers;
+    }
+
+    public String getNotifyUsers() {
+        return notifyUsers;
+    }
+
+    public void setDeptId(Long deptId) {
+        this.deptId = deptId;
+    }
+
+    public Long getDeptId() {
+        return deptId;
+    }
+
+    public void setDeptName(String deptName) {
+        this.deptName = deptName;
+    }
+
+    public String getDeptName() {
+        return deptName;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+                .append("alertId", getAlertId())
+                .append("vehicleId", getVehicleId())
+                .append("vehicleNo", getVehicleNo())
+                .append("alertDate", getAlertDate())
+                .append("alertTime", getAlertTime())
+                .append("mileage", getMileage())
+                .append("alertType", getAlertType())
+                .append("alertReason", getAlertReason())
+                .append("startTime", getStartTime())
+                .append("endTime", getEndTime())
+                .append("alertCount", getAlertCount())
+                .append("status", getStatus())
+                .append("handlerId", getHandlerId())
+                .append("handlerName", getHandlerName())
+                .append("handleTime", getHandleTime())
+                .append("handleRemark", getHandleRemark())
+                .append("notifyStatus", getNotifyStatus())
+                .append("notifyTime", getNotifyTime())
+                .append("notifyUsers", getNotifyUsers())
+                .append("deptId", getDeptId())
+                .append("deptName", getDeptName())
+                .append("createTime", getCreateTime())
+                .append("updateTime", getUpdateTime())
+                .toString();
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleAlertConfig.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleAlertConfig.java
new file mode 100644
index 0000000..01e2070
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleAlertConfig.java
@@ -0,0 +1,156 @@
+package com.ruoyi.system.domain;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 杞﹁締寮傚父鍛婅閰嶇疆瀵硅薄 tb_vehicle_alert_config
+ * 
+ * @author ruoyi
+ */
+public class VehicleAlertConfig extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /** 閰嶇疆ID */
+    private Long configId;
+
+    /** 閰嶇疆绫诲瀷(GLOBAL-鍏ㄥ眬/DEPT-閮ㄩ棬/VEHICLE-杞﹁締) */
+    @Excel(name = "閰嶇疆绫诲瀷", readConverterExp = "GLOBAL=鍏ㄥ眬,DEPT=閮ㄩ棬,VEHICLE=杞﹁締")
+    private String configType;
+
+    /** 閮ㄩ棬ID锛堥儴闂ㄩ厤缃椂浣跨敤锛� */
+    @Excel(name = "閮ㄩ棬ID")
+    private Long deptId;
+
+    /** 杞﹁締ID锛堣溅杈嗛厤缃椂浣跨敤锛� */
+    @Excel(name = "杞﹁締ID")
+    private Long vehicleId;
+
+    /** 鐩爣鍚嶇О锛堥儴闂ㄥ悕绉版垨杞︾墝鍙凤級 */
+    private String targetName;
+
+    /** 鍏噷鏁板憡璀﹂槇鍊�(鍏噷) */
+    @Excel(name = "鍏噷鏁伴槇鍊�")
+    private BigDecimal mileageThreshold;
+
+    /** 姣忔棩鏈�澶у憡璀︽鏁� */
+    @Excel(name = "姣忔棩鍛婅娆℃暟")
+    private Integer dailyAlertLimit;
+
+    /** 鍛婅闂撮殧鏃堕棿(鍒嗛挓) */
+    @Excel(name = "鍛婅闂撮殧(鍒嗛挓)")
+    private Integer alertInterval;
+
+    /** 閫氱煡鐢ㄦ埛ID鍒楄〃锛堥�楀彿鍒嗛殧锛� */
+    @Excel(name = "閫氱煡鐢ㄦ埛")
+    private String notifyUserIds;
+
+    /** 鐘舵�侊紙0-鍚敤 1-鍋滅敤锛� */
+    @Excel(name = "鐘舵��", readConverterExp = "0=鍚敤,1=鍋滅敤")
+    private String status;
+
+    public void setConfigId(Long configId) {
+        this.configId = configId;
+    }
+
+    public Long getConfigId() {
+        return configId;
+    }
+
+    public void setConfigType(String configType) {
+        this.configType = configType;
+    }
+
+    public String getConfigType() {
+        return configType;
+    }
+
+    public void setDeptId(Long deptId) {
+        this.deptId = deptId;
+    }
+
+    public Long getDeptId() {
+        return deptId;
+    }
+
+    public void setVehicleId(Long vehicleId) {
+        this.vehicleId = vehicleId;
+    }
+
+    public Long getVehicleId() {
+        return vehicleId;
+    }
+
+    public void setTargetName(String targetName) {
+        this.targetName = targetName;
+    }
+
+    public String getTargetName() {
+        return targetName;
+    }
+
+    public void setMileageThreshold(BigDecimal mileageThreshold) {
+        this.mileageThreshold = mileageThreshold;
+    }
+
+    public BigDecimal getMileageThreshold() {
+        return mileageThreshold;
+    }
+
+    public void setDailyAlertLimit(Integer dailyAlertLimit) {
+        this.dailyAlertLimit = dailyAlertLimit;
+    }
+
+    public Integer getDailyAlertLimit() {
+        return dailyAlertLimit;
+    }
+
+    public void setAlertInterval(Integer alertInterval) {
+        this.alertInterval = alertInterval;
+    }
+
+    public Integer getAlertInterval() {
+        return alertInterval;
+    }
+
+    public void setNotifyUserIds(String notifyUserIds) {
+        this.notifyUserIds = notifyUserIds;
+    }
+
+    public String getNotifyUserIds() {
+        return notifyUserIds;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+                .append("configId", getConfigId())
+                .append("configType", getConfigType())
+                .append("deptId", getDeptId())
+                .append("vehicleId", getVehicleId())
+                .append("targetName", getTargetName())
+                .append("mileageThreshold", getMileageThreshold())
+                .append("dailyAlertLimit", getDailyAlertLimit())
+                .append("alertInterval", getAlertInterval())
+                .append("notifyUserIds", getNotifyUserIds())
+                .append("status", getStatus())
+                .append("createBy", getCreateBy())
+                .append("createTime", getCreateTime())
+                .append("updateBy", getUpdateBy())
+                .append("updateTime", getUpdateTime())
+                .append("remark", getRemark())
+                .toString();
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/VehicleSyncVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/VehicleSyncVO.java
new file mode 100644
index 0000000..637c1fb
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/VehicleSyncVO.java
@@ -0,0 +1,100 @@
+package com.ruoyi.system.domain.vo;
+
+/**
+ * 杞﹁締鍚屾瑙嗗浘瀵硅薄
+ * 鐢ㄤ簬鍓嶇鏄剧ず鏃х郴缁熻溅杈嗘暟鎹強鍚屾鐘舵��
+ * 
+ * @author ruoyi
+ */
+public class VehicleSyncVO {
+    
+    /** 鏃х郴缁熻溅杈咺D */
+    private Integer carId;
+    
+    /** 杞︾墝鍙� */
+    private String vehicleNo;
+    
+    /** 杞﹁締鍗曟嵁绫诲瀷缂栫爜 */
+    private String carOrdClass;
+    
+    /** 褰掑睘鍒嗗叕鍙革紙浠庢棫绯荤粺鑾峰彇鐨勫崟鎹被鍨嬫槧灏勶級 */
+    private String deptName;
+    
+    /** 鏄惁宸插悓姝ュ埌鏂扮郴缁� */
+    private Boolean synced;
+    
+    /** 鏂扮郴缁熶腑鐨勮溅杈咺D锛堝凡鍚屾鏃舵湁鍊硷級 */
+    private Long vehicleId;
+    
+    /** 鏂扮郴缁熶腑鐨勯儴闂↖D锛堝凡鍚屾鏃舵湁鍊硷級 */
+    private Long deptId;
+
+    public Integer getCarId() {
+        return carId;
+    }
+
+    public void setCarId(Integer carId) {
+        this.carId = carId;
+    }
+
+    public String getVehicleNo() {
+        return vehicleNo;
+    }
+
+    public void setVehicleNo(String vehicleNo) {
+        this.vehicleNo = vehicleNo;
+    }
+
+    public String getCarOrdClass() {
+        return carOrdClass;
+    }
+
+    public void setCarOrdClass(String carOrdClass) {
+        this.carOrdClass = carOrdClass;
+    }
+
+    public String getDeptName() {
+        return deptName;
+    }
+
+    public void setDeptName(String deptName) {
+        this.deptName = deptName;
+    }
+
+    public Boolean getSynced() {
+        return synced;
+    }
+
+    public void setSynced(Boolean synced) {
+        this.synced = synced;
+    }
+
+    public Long getVehicleId() {
+        return vehicleId;
+    }
+
+    public void setVehicleId(Long vehicleId) {
+        this.vehicleId = vehicleId;
+    }
+
+    public Long getDeptId() {
+        return deptId;
+    }
+
+    public void setDeptId(Long deptId) {
+        this.deptId = deptId;
+    }
+
+    @Override
+    public String toString() {
+        return "VehicleSyncVO{" +
+                "carId=" + carId +
+                ", vehicleNo='" + vehicleNo + '\'' +
+                ", carOrdClass='" + carOrdClass + '\'' +
+                ", deptName='" + deptName + '\'' +
+                ", synced=" + synced +
+                ", vehicleId=" + vehicleId +
+                ", deptId=" + deptId +
+                '}';
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/listener/TaskMessageListener.java b/ruoyi-system/src/main/java/com/ruoyi/system/listener/TaskMessageListener.java
index d343005..88bfce9 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/listener/TaskMessageListener.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/listener/TaskMessageListener.java
@@ -83,7 +83,7 @@
                    sendDispatchNotify(assigneeIds, task.getCreatorId(), event.getTaskId(),task.getShowTaskCode(), buildNotifyContent(task, emergency));
                }
             }
-
+            syncDispatchActualStartTime(emergency, task);
             Long taskId= event.getTaskId();
             Long dispatchOrdId= event.getDispatchOrderId();
             Long serviceOrdId= event.getServiceOrderId();
@@ -94,6 +94,19 @@
             log.error("澶勭悊浠诲姟娲惧彂鍚屾浜嬩欢澶辫触", ex);
         }
     }
+
+    private void syncDispatchActualStartTime(SysTaskEmergency emergency, SysTask task) {
+        try {
+            //杩欓噷涔熷悓姝ヤ竴涓嬪疄闄呮椂闂�
+            Long disatpchOrdId = emergency.getLegacyDispatchOrdId();
+            Date actualTime = task.getActualStartTime();
+            legacySystemSyncService.updateDispatchActualTime(disatpchOrdId, actualTime);
+        }catch (Exception ex){
+        log.error("鍚屾瀹為檯鏃堕棿澶辫触", ex);
+        }
+    }
+
+
     /**
      * 鐩戝惉浠诲姟鍒涘缓浜嬩欢
      * 
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/DispatchOrdMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/DispatchOrdMapper.java
index bf5be21..5db1a6e 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/DispatchOrdMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/DispatchOrdMapper.java
@@ -100,4 +100,14 @@
     public int updateDispatchOrdCancelReason(@Param("dispatchOrdID") Long dispatchOrdID, 
                                              @Param("cancelReasonId") Integer cancelReasonId,
                                              @Param("cancelReasonText") String cancelReasonText);
+    
+    /**
+     * 鏇存柊璋冨害鍗曞疄闄呭紑濮嬫椂闂�
+     * 
+     * @param dispatchOrdID 璋冨害鍗旾D
+     * @param actualDate 瀹為檯寮�濮嬫椂闂�
+     * @return 褰卞搷琛屾暟
+     */
+    public int updateDispatchOrdActualDate(@Param("dispatchOrdID") Long dispatchOrdID, 
+                                           @Param("actualDate") java.util.Date actualDate);
 } 
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTaskMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTaskMapper.java
index fbd9c61..cd4b187 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTaskMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTaskMapper.java
@@ -149,4 +149,12 @@
      * @return 浠诲姟鏁伴噺
      */
     public int countTaskByPhoneAndDate(@Param("phone") String phone, @Param("createDate") String createDate);
+    
+    /**
+     * 鏌ヨ杞﹁締鍦ㄦ寚瀹氭椂闂磋寖鍥村唴鐨勪换鍔″垪琛�
+     * 
+     * @param params 鍖呭惈vehicleId銆乻tartTime銆乪ndTime鐨勫弬鏁癕ap
+     * @return 浠诲姟鍒楄〃
+     */
+    public List<SysTask> selectVehicleTasksInTimeRange(java.util.Map<String, Object> params);
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbHospDataMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbHospDataMapper.java
index 91f52c7..0a08f0d 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbHospDataMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbHospDataMapper.java
@@ -67,4 +67,14 @@
      * @return 缁撴灉
      */
     int deleteTbHospDataByIds(@Param("hospIds") Long[] hospIds);
+    
+    /**
+     * 鏍规嵁鍒嗚瘝鍏抽敭璇嶉杩囨护鍖婚櫌鏁版嵁
+     * 閫氳繃鏁版嵁搴撳眰闈㈢殑LIKE鍖归厤蹇�熺瓫閫夊嚭鍙兘鍖归厤鐨勫尰闄�
+     * 
+     * @param keywords 鍒嗚瘝鍏抽敭璇嶅垪琛�
+     * @param status 鍖婚櫌鐘舵�侊紙0-姝e父锛�
+     * @return 鍖婚櫌鏁版嵁闆嗗悎
+     */
+    List<TbHospData> selectTbHospDataByKeywords(@Param("keywords") List<String> keywords, @Param("status") String status);
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleAbnormalAlertMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleAbnormalAlertMapper.java
new file mode 100644
index 0000000..fb5d76c
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleAbnormalAlertMapper.java
@@ -0,0 +1,92 @@
+package com.ruoyi.system.mapper;
+
+import java.util.Date;
+import java.util.List;
+import com.ruoyi.system.domain.VehicleAbnormalAlert;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 杞﹁締寮傚父鍛婅Mapper鎺ュ彛
+ * 
+ * @author ruoyi
+ */
+public interface VehicleAbnormalAlertMapper {
+    /**
+     * 鏌ヨ杞﹁締寮傚父鍛婅
+     * 
+     * @param alertId 杞﹁締寮傚父鍛婅涓婚敭
+     * @return 杞﹁締寮傚父鍛婅
+     */
+    public VehicleAbnormalAlert selectVehicleAbnormalAlertByAlertId(Long alertId);
+
+    /**
+     * 鏌ヨ杞﹁締寮傚父鍛婅鍒楄〃
+     * 
+     * @param vehicleAbnormalAlert 杞﹁締寮傚父鍛婅
+     * @return 杞﹁締寮傚父鍛婅闆嗗悎
+     */
+    public List<VehicleAbnormalAlert> selectVehicleAbnormalAlertList(VehicleAbnormalAlert vehicleAbnormalAlert);
+
+    /**
+     * 鏂板杞﹁締寮傚父鍛婅
+     * 
+     * @param vehicleAbnormalAlert 杞﹁締寮傚父鍛婅
+     * @return 缁撴灉
+     */
+    public int insertVehicleAbnormalAlert(VehicleAbnormalAlert vehicleAbnormalAlert);
+
+    /**
+     * 淇敼杞﹁締寮傚父鍛婅
+     * 
+     * @param vehicleAbnormalAlert 杞﹁締寮傚父鍛婅
+     * @return 缁撴灉
+     */
+    public int updateVehicleAbnormalAlert(VehicleAbnormalAlert vehicleAbnormalAlert);
+
+    /**
+     * 鍒犻櫎杞﹁締寮傚父鍛婅
+     * 
+     * @param alertId 杞﹁締寮傚父鍛婅涓婚敭
+     * @return 缁撴灉
+     */
+    public int deleteVehicleAbnormalAlertByAlertId(Long alertId);
+
+    /**
+     * 鎵归噺鍒犻櫎杞﹁締寮傚父鍛婅
+     * 
+     * @param alertIds 闇�瑕佸垹闄ょ殑鏁版嵁涓婚敭闆嗗悎
+     * @return 缁撴灉
+     */
+    public int deleteVehicleAbnormalAlertByAlertIds(Long[] alertIds);
+
+    /**
+     * 鏌ヨ杞﹁締褰撴棩鍛婅娆℃暟
+     * 
+     * @param vehicleId 杞﹁締ID
+     * @param alertDate 鍛婅鏃ユ湡
+     * @return 鍛婅娆℃暟
+     */
+    public int selectDailyAlertCount(@Param("vehicleId") Long vehicleId, @Param("alertDate") Date alertDate);
+
+    /**
+     * 鏌ヨ杞﹁締鏈�鍚庝竴娆″憡璀︽椂闂�
+     * 
+     * @param vehicleId 杞﹁締ID
+     * @return 鏈�鍚庡憡璀︽椂闂�
+     */
+    public Date selectLastAlertTime(@Param("vehicleId") Long vehicleId);
+
+    /**
+     * 鎵归噺澶勭悊鍛婅
+     * 
+     * @param alertIds 鍛婅ID鍒楄〃
+     * @param handlerId 澶勭悊浜篒D
+     * @param handlerName 澶勭悊浜哄鍚�
+     * @param handleRemark 澶勭悊澶囨敞
+     * @return 缁撴灉
+     */
+    public int batchHandleAlert(@Param("alertIds") Long[] alertIds, 
+                                 @Param("handlerId") Long handlerId, 
+                                 @Param("handlerName") String handlerName, 
+                                 @Param("handleRemark") String handleRemark);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleAlertConfigMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleAlertConfigMapper.java
new file mode 100644
index 0000000..7faeb5b
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleAlertConfigMapper.java
@@ -0,0 +1,71 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+import org.apache.ibatis.annotations.Param;
+import com.ruoyi.system.domain.VehicleAlertConfig;
+
+/**
+ * 杞﹁締鍛婅閰嶇疆Mapper鎺ュ彛
+ * 
+ * @author ruoyi
+ * @date 2026-01-12
+ */
+public interface VehicleAlertConfigMapper 
+{
+    /**
+     * 鏌ヨ杞﹁締鍛婅閰嶇疆
+     * 
+     * @param configId 杞﹁締鍛婅閰嶇疆涓婚敭
+     * @return 杞﹁締鍛婅閰嶇疆
+     */
+    public VehicleAlertConfig selectVehicleAlertConfigByConfigId(Long configId);
+
+    /**
+     * 鏌ヨ杞﹁締鍛婅閰嶇疆鍒楄〃
+     * 
+     * @param vehicleAlertConfig 杞﹁締鍛婅閰嶇疆
+     * @return 杞﹁締鍛婅閰嶇疆闆嗗悎
+     */
+    public List<VehicleAlertConfig> selectVehicleAlertConfigList(VehicleAlertConfig vehicleAlertConfig);
+
+    /**
+     * 鏌ヨ杞﹁締鐨勯厤缃紙浼樺厛绾э細杞﹁締 > 閮ㄩ棬 > 鍏ㄥ眬锛�
+     * 
+     * @param vehicleId 杞﹁締ID
+     * @param deptId 閮ㄩ棬ID
+     * @return 杞﹁締鍛婅閰嶇疆
+     */
+    public VehicleAlertConfig selectConfigByVehicle(@Param("vehicleId") Long vehicleId, @Param("deptId") Long deptId);
+
+    /**
+     * 鏂板杞﹁締鍛婅閰嶇疆
+     * 
+     * @param vehicleAlertConfig 杞﹁締鍛婅閰嶇疆
+     * @return 缁撴灉
+     */
+    public int insertVehicleAlertConfig(VehicleAlertConfig vehicleAlertConfig);
+
+    /**
+     * 淇敼杞﹁締鍛婅閰嶇疆
+     * 
+     * @param vehicleAlertConfig 杞﹁締鍛婅閰嶇疆
+     * @return 缁撴灉
+     */
+    public int updateVehicleAlertConfig(VehicleAlertConfig vehicleAlertConfig);
+
+    /**
+     * 鍒犻櫎杞﹁締鍛婅閰嶇疆
+     * 
+     * @param configId 杞﹁締鍛婅閰嶇疆涓婚敭
+     * @return 缁撴灉
+     */
+    public int deleteVehicleAlertConfigByConfigId(Long configId);
+
+    /**
+     * 鎵归噺鍒犻櫎杞﹁締鍛婅閰嶇疆
+     * 
+     * @param configIds 闇�瑕佸垹闄ょ殑鏁版嵁涓婚敭闆嗗悎
+     * @return 缁撴灉
+     */
+    public int deleteVehicleAlertConfigByConfigIds(Long[] configIds);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleGpsSegmentMileageMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleGpsSegmentMileageMapper.java
index 3314f93..8a8d46b 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleGpsSegmentMileageMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleGpsSegmentMileageMapper.java
@@ -84,4 +84,11 @@
      */
     public Long selectLastCalculatedGpsId(@Param("vehicleId") Long vehicleId,
                                            @Param("beforeTime") Date beforeTime);
+    
+    /**
+     * 鏌ヨ杞﹁締鍦ㄦ寚瀹氭椂闂磋寖鍥村唴鐨勫垎娈甸噷绋嬭褰�
+     * @param params 鍖呭惈vehicleId銆乻tartTime銆乪ndTime鐨勫弬鏁癕ap
+     * @return 鍒嗘閲岀▼璁板綍鍒楄〃
+     */
+    public List<VehicleGpsSegmentMileage> selectSegmentsByTimeRange(java.util.Map<String, Object> params);
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/HospitalTokenizerAsyncService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/HospitalTokenizerAsyncService.java
new file mode 100644
index 0000000..6a8dbef
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/HospitalTokenizerAsyncService.java
@@ -0,0 +1,149 @@
+package com.ruoyi.system.service;
+
+import com.ruoyi.system.domain.HospitalTokenizerTask;
+import com.ruoyi.system.domain.TbHospData;
+import com.ruoyi.system.mapper.TbHospDataMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 鍖婚櫌鍒嗚瘝寮傛浠诲姟鏈嶅姟
+ * 
+ * @author ruoyi
+ * @date 2026-01-20
+ */
+@Service
+public class HospitalTokenizerAsyncService {
+    
+    private static final Logger logger = LoggerFactory.getLogger(HospitalTokenizerAsyncService.class);
+    
+    @Autowired
+    private ITbHospDataService tbHospDataService;
+    
+    @Autowired
+    private TbHospDataMapper tbHospDataMapper;
+    
+    /**
+     * 浠诲姟鐘舵�佺紦瀛� (鐢熶骇鐜寤鸿浣跨敤 Redis)
+     */
+    private static final Map<String, HospitalTokenizerTask> taskCache = new ConcurrentHashMap<>();
+    
+    /**
+     * 寮傛鎵ц鍖婚櫌鍒嗚瘝浠诲姟
+     * 
+     * @param taskId 浠诲姟ID
+     */
+    @Async("taskExecutor")
+    public void executeTokenizerTask(String taskId) {
+        HospitalTokenizerTask task = new HospitalTokenizerTask(taskId);
+        taskCache.put(taskId, task);
+        
+        logger.info("寮�濮嬫墽琛屽尰闄㈠垎璇嶅紓姝ヤ换鍔�: taskId={}", taskId);
+        
+        try {
+            // 鏌ヨ鎵�鏈夋甯哥姸鎬佺殑鍖婚櫌
+            TbHospData query = new TbHospData();
+            query.setStatus("0");
+            List<TbHospData> hospitalList = tbHospDataMapper.selectTbHospDataList(query);
+            
+            task.setTotalCount(hospitalList.size());
+            logger.info("鏌ヨ鍒� {} 涓尰闄㈤渶瑕佺敓鎴愬垎璇�", hospitalList.size());
+            
+            int successCount = 0;
+            int failedCount = 0;
+            
+            // 閬嶅巻澶勭悊姣忎釜鍖婚櫌
+            for (int i = 0; i < hospitalList.size(); i++) {
+                TbHospData hospital = hospitalList.get(i);
+                
+                try {
+                    // 鐢熸垚鍒嗚瘝
+                    String keywords = tbHospDataService.generateKeywordsForHospital(hospital);
+                    hospital.setHospKeywords(keywords);
+                    
+                    // 鏇存柊鏁版嵁搴�
+                    int result = tbHospDataMapper.updateTbHospData(hospital);
+                    if (result > 0) {
+                        successCount++;
+                    } else {
+                        failedCount++;
+                    }
+                    
+                } catch (Exception e) {
+                    failedCount++;
+                    logger.error("鐢熸垚鍖婚櫌鍒嗚瘝澶辫触: hospId={}, hospName={}", 
+                        hospital.getHospId(), hospital.getHospName(), e);
+                }
+                
+                // 鏇存柊浠诲姟杩涘害
+                task.setProcessedCount(i + 1);
+                task.setSuccessCount(successCount);
+                task.setFailedCount(failedCount);
+                
+                // 姣忓鐞�100鏉¤緭鍑轰竴娆℃棩蹇�
+                if ((i + 1) % 100 == 0) {
+                    logger.info("鍖婚櫌鍒嗚瘝杩涘害: {}/{}, 鎴愬姛: {}, 澶辫触: {}", 
+                        i + 1, hospitalList.size(), successCount, failedCount);
+                }
+            }
+            
+            // 浠诲姟瀹屾垚
+            task.setStatus("SUCCESS");
+            task.setEndTime(new Date());
+            
+            logger.info("鍖婚櫌鍒嗚瘝浠诲姟瀹屾垚: taskId={}, 鎬绘暟: {}, 鎴愬姛: {}, 澶辫触: {}", 
+                taskId, hospitalList.size(), successCount, failedCount);
+            
+        } catch (Exception e) {
+            // 浠诲姟澶辫触
+            task.setStatus("FAILED");
+            task.setEndTime(new Date());
+            task.setErrorMessage(e.getMessage());
+            
+            logger.error("鍖婚櫌鍒嗚瘝浠诲姟鎵ц澶辫触: taskId={}", taskId, e);
+        }
+    }
+    
+    /**
+     * 鑾峰彇浠诲姟鐘舵��
+     * 
+     * @param taskId 浠诲姟ID
+     * @return 浠诲姟鐘舵��
+     */
+    public HospitalTokenizerTask getTaskStatus(String taskId) {
+        return taskCache.get(taskId);
+    }
+    
+    /**
+     * 娓呯悊浠诲姟缂撳瓨
+     * 
+     * @param taskId 浠诲姟ID
+     */
+    public void clearTask(String taskId) {
+        taskCache.remove(taskId);
+    }
+    
+    /**
+     * 娓呯悊鎵�鏈夊凡瀹屾垚鐨勪换鍔� (瓒呰繃1灏忔椂)
+     */
+    public void clearExpiredTasks() {
+        long oneHourAgo = System.currentTimeMillis() - 3600000;
+        
+        taskCache.entrySet().removeIf(entry -> {
+            HospitalTokenizerTask task = entry.getValue();
+            if (task.getEndTime() != null && task.getEndTime().getTime() < oneHourAgo) {
+                logger.info("娓呯悊杩囨湡浠诲姟: taskId={}", entry.getKey());
+                return true;
+            }
+            return false;
+        });
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/IDispatchOrdService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/IDispatchOrdService.java
index 3e3efa0..a954fc3 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/IDispatchOrdService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/IDispatchOrdService.java
@@ -88,4 +88,13 @@
     public int updateDispatchOrdState(Long dispatchOrdID, Integer dispatchOrdState);
 
     public void cancelDispatchOrd(Long dispatchOrdID,Integer cancelReason,String cancelCReasonTxt);
+    
+    /**
+     * 鏇存柊璋冨害鍗曞疄闄呭紑濮嬫椂闂�
+     * 
+     * @param dispatchOrdID 璋冨害鍗旾D
+     * @param actualDate 瀹為檯寮�濮嬫椂闂�
+     * @return 褰卞搷琛屾暟
+     */
+    public int updateDispatchOrdActualDate(Long dispatchOrdID, java.util.Date actualDate);
 } 
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ILegacySystemSyncService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ILegacySystemSyncService.java
index 7187a02..7ad1705 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ILegacySystemSyncService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ILegacySystemSyncService.java
@@ -2,6 +2,8 @@
 
 import com.ruoyi.system.domain.SysTask;
 
+import java.util.Date;
+
 /**
  * 鏃х郴缁熷悓姝ervice鎺ュ彛
  * 
@@ -10,7 +12,13 @@
  */
 public interface ILegacySystemSyncService {
 
-
+    /**
+     * 鏇存柊璋冨害鍗曞疄闄呭畬鎴愭椂闂�
+     * @param dispatchOrdId
+     * @param actualTime
+     * @return
+     */
+    Integer updateDispatchActualTime(Long dispatchOrdId, Date actualTime);
     /**
      * 鍚屾鎬ユ晳杞繍浠诲姟鍒版棫绯荤粺
      * 
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/IQyWechatService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/IQyWechatService.java
index a3e6c13..22a7059 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/IQyWechatService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/IQyWechatService.java
@@ -19,6 +19,16 @@
     boolean sendNotifyMessage(Long userId, String title, String content, String notifyUrl);
 
     /**
+     * 鍙戦�佷紒涓氬井淇℃秷鎭紝甯﹂粯璁ゅ簲鐢↖D
+     * @param userId 鐢ㄦ埛ID
+     * @param title 娑堟伅鏍囬
+     * @param content 娑堟伅鍐呭
+     * @param businessUrl 灏忕▼搴忚闂矾寰�
+     * @return
+     */
+    boolean sendNotifyMessageWithDefaultAppId(Long userId, String title, String content, String businessUrl);
+
+    /**
      * 鍙戦�佷紒涓氬井淇℃秷鎭紝甯﹀皬绋嬪簭璺緞閾炬帴
      * @param userId
      * @param title
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskService.java
index ce9ae98..3def5e1 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskService.java
@@ -59,19 +59,29 @@
      * @param createVO 浠诲姟鍒涘缓瀵硅薄
      * @return 缁撴灉
      */
-    public int insertSysTask(TaskCreateVO createVO);
+    /**
+     * 鏂板浠诲姟
+     * 
+     * @param createVO 浠诲姟鍒涘缓淇℃伅
+     * @return 浠诲姟ID
+     */
+    public Long insertSysTask(TaskCreateVO createVO);
 
     /**
      * 鏂板浠诲姟绠$悊锛堝厑璁镐粠澶栭儴浼犲叆鐢ㄦ埛淇℃伅銆侀儴闂ㄤ俊鎭拰鏃堕棿淇℃伅锛�
      * 
      * @param createVO 浠诲姟鍒涘缓瀵硅薄
+     * @param serviceOrderId 鏈嶅姟鍗旾D
+     * @param dispatchOrderId 璋冨害鍗旾D
+     * @param serviceOrdNo 鏈嶅姟鍗曠紪鍙�
      * @param userId 鐢ㄦ埛ID
+     * @param userName 鐢ㄦ埛鍚嶇О
      * @param deptId 閮ㄩ棬ID
      * @param createTime 鍒涘缓鏃堕棿
      * @param updateTime 鏇存柊鏃堕棿
-     * @return 缁撴灉
+     * @return 浠诲姟ID
      */
-    public int insertTask(TaskCreateVO createVO,Long serviceOrderId,Long dispatchOrderId, String serviceOrdNo, Long userId,String userName, Long deptId, Date createTime, Date updateTime);
+    public Long insertTask(TaskCreateVO createVO,Long serviceOrderId,Long dispatchOrderId, String serviceOrdNo, Long userId,String userName, Long deptId, Date createTime, Date updateTime);
 
     /**
      * 淇敼浠诲姟绠$悊
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ITbHospDataService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ITbHospDataService.java
index 79817f9..da508bb 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ITbHospDataService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ITbHospDataService.java
@@ -66,4 +66,20 @@
      * @return 缁撴灉
      */
     int deleteTbHospDataById(Long hospId);
+
+    /**
+     * 鎵归噺鐢熸垚骞舵洿鏂版墍鏈夊尰闄㈢殑鍒嗚瘝
+     * 渚涘尰闄㈠悓姝ユ椂璋冪敤
+     * 
+     * @return 鏇存柊鐨勫尰闄㈡暟閲�
+     */
+    int generateAllHospitalKeywords();
+
+    /**
+     * 涓哄崟涓尰闄㈢敓鎴愬垎璇�
+     * 
+     * @param tbHospData 鍖婚櫌鏁版嵁
+     * @return 鐢熸垚鐨勫垎璇�
+     */
+    String generateKeywordsForHospital(TbHospData tbHospData);
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleAbnormalAlertService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleAbnormalAlertService.java
new file mode 100644
index 0000000..66f5e81
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleAbnormalAlertService.java
@@ -0,0 +1,97 @@
+package com.ruoyi.system.service;
+
+import java.util.Date;
+import java.util.List;
+import com.ruoyi.system.domain.VehicleAbnormalAlert;
+
+/**
+ * 杞﹁締寮傚父鍛婅Service鎺ュ彛
+ * 
+ * @author ruoyi
+ */
+public interface IVehicleAbnormalAlertService {
+    /**
+     * 鏌ヨ杞﹁締寮傚父鍛婅
+     * 
+     * @param alertId 杞﹁締寮傚父鍛婅涓婚敭
+     * @return 杞﹁締寮傚父鍛婅
+     */
+    public VehicleAbnormalAlert selectVehicleAbnormalAlertByAlertId(Long alertId);
+
+    /**
+     * 鏌ヨ杞﹁締寮傚父鍛婅鍒楄〃
+     * 
+     * @param vehicleAbnormalAlert 杞﹁締寮傚父鍛婅
+     * @return 杞﹁締寮傚父鍛婅闆嗗悎
+     */
+    public List<VehicleAbnormalAlert> selectVehicleAbnormalAlertList(VehicleAbnormalAlert vehicleAbnormalAlert);
+
+    /**
+     * 鏂板杞﹁締寮傚父鍛婅
+     * 
+     * @param vehicleAbnormalAlert 杞﹁締寮傚父鍛婅
+     * @return 缁撴灉
+     */
+    public int insertVehicleAbnormalAlert(VehicleAbnormalAlert vehicleAbnormalAlert);
+
+    /**
+     * 淇敼杞﹁締寮傚父鍛婅
+     * 
+     * @param vehicleAbnormalAlert 杞﹁締寮傚父鍛婅
+     * @return 缁撴灉
+     */
+    public int updateVehicleAbnormalAlert(VehicleAbnormalAlert vehicleAbnormalAlert);
+
+    /**
+     * 鎵归噺鍒犻櫎杞﹁締寮傚父鍛婅
+     * 
+     * @param alertIds 闇�瑕佸垹闄ょ殑杞﹁締寮傚父鍛婅涓婚敭闆嗗悎
+     * @return 缁撴灉
+     */
+    public int deleteVehicleAbnormalAlertByAlertIds(Long[] alertIds);
+
+    /**
+     * 鍒犻櫎杞﹁締寮傚父鍛婅淇℃伅
+     * 
+     * @param alertId 杞﹁締寮傚父鍛婅涓婚敭
+     * @return 缁撴灉
+     */
+    public int deleteVehicleAbnormalAlertByAlertId(Long alertId);
+
+    /**
+     * 澶勭悊鍛婅
+     * 
+     * @param alertId 鍛婅ID
+     * @param handlerId 澶勭悊浜篒D
+     * @param handlerName 澶勭悊浜哄鍚�
+     * @param handleRemark 澶勭悊澶囨敞
+     * @return 缁撴灉
+     */
+    public int handleAlert(Long alertId, Long handlerId, String handlerName, String handleRemark);
+
+    /**
+     * 鎵归噺澶勭悊鍛婅
+     * 
+     * @param alertIds 鍛婅ID鍒楄〃
+     * @param handlerId 澶勭悊浜篒D
+     * @param handlerName 澶勭悊浜哄鍚�
+     * @param handleRemark 澶勭悊澶囨敞
+     * @return 缁撴灉
+     */
+    public int batchHandleAlert(Long[] alertIds, Long handlerId, String handlerName, String handleRemark);
+
+    /**
+     * 妫�鏌ュ苟鍒涘缓杞﹁締寮傚父鍛婅
+     * 
+     * @param vehicleId 杞﹁締ID
+     * @param vehicleNo 杞︾墝鍙�
+     * @param mileage 杩愯鍏噷鏁�
+     * @param startTime 寮�濮嬫椂闂�
+     * @param endTime 缁撴潫鏃堕棿
+     * @param deptId 閮ㄩ棬ID
+     * @param deptName 閮ㄩ棬鍚嶇О
+     * @return 鏄惁鍒涘缓鎴愬姛
+     */
+    public boolean checkAndCreateAlert(Long vehicleId, String vehicleNo, java.math.BigDecimal mileage, 
+                                       Date startTime, Date endTime, Long deptId, String deptName);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleAlertConfigService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleAlertConfigService.java
new file mode 100644
index 0000000..8952322
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleAlertConfigService.java
@@ -0,0 +1,70 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import com.ruoyi.system.domain.VehicleAlertConfig;
+
+/**
+ * 杞﹁締鍛婅閰嶇疆Service鎺ュ彛
+ * 
+ * @author ruoyi
+ * @date 2026-01-12
+ */
+public interface IVehicleAlertConfigService 
+{
+    /**
+     * 鏌ヨ杞﹁締鍛婅閰嶇疆
+     * 
+     * @param configId 杞﹁締鍛婅閰嶇疆涓婚敭
+     * @return 杞﹁締鍛婅閰嶇疆
+     */
+    public VehicleAlertConfig selectVehicleAlertConfigByConfigId(Long configId);
+
+    /**
+     * 鏌ヨ杞﹁締鍛婅閰嶇疆鍒楄〃
+     * 
+     * @param vehicleAlertConfig 杞﹁締鍛婅閰嶇疆
+     * @return 杞﹁締鍛婅閰嶇疆闆嗗悎
+     */
+    public List<VehicleAlertConfig> selectVehicleAlertConfigList(VehicleAlertConfig vehicleAlertConfig);
+
+    /**
+     * 鑾峰彇杞﹁締鐨勫憡璀﹂厤缃紙浼樺厛绾э細杞﹁締 > 閮ㄩ棬 > 鍏ㄥ眬锛�
+     * 
+     * @param vehicleId 杞﹁締ID
+     * @param deptId 閮ㄩ棬ID
+     * @return 杞﹁締鍛婅閰嶇疆
+     */
+    public VehicleAlertConfig getConfigByVehicle(Long vehicleId, Long deptId);
+
+    /**
+     * 鏂板杞﹁締鍛婅閰嶇疆
+     * 
+     * @param vehicleAlertConfig 杞﹁締鍛婅閰嶇疆
+     * @return 缁撴灉
+     */
+    public int insertVehicleAlertConfig(VehicleAlertConfig vehicleAlertConfig);
+
+    /**
+     * 淇敼杞﹁締鍛婅閰嶇疆
+     * 
+     * @param vehicleAlertConfig 杞﹁締鍛婅閰嶇疆
+     * @return 缁撴灉
+     */
+    public int updateVehicleAlertConfig(VehicleAlertConfig vehicleAlertConfig);
+
+    /**
+     * 鎵归噺鍒犻櫎杞﹁締鍛婅閰嶇疆
+     * 
+     * @param configIds 闇�瑕佸垹闄ょ殑杞﹁締鍛婅閰嶇疆涓婚敭闆嗗悎
+     * @return 缁撴灉
+     */
+    public int deleteVehicleAlertConfigByConfigIds(Long[] configIds);
+
+    /**
+     * 鍒犻櫎杞﹁締鍛婅閰嶇疆淇℃伅
+     * 
+     * @param configId 杞﹁締鍛婅閰嶇疆涓婚敭
+     * @return 缁撴灉
+     */
+    public int deleteVehicleAlertConfigByConfigId(Long configId);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/DispatchOrdServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/DispatchOrdServiceImpl.java
index 9a61dbb..471cea1 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/DispatchOrdServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/DispatchOrdServiceImpl.java
@@ -126,6 +126,18 @@
     public void cancelDispatchOrd(Long dispatchOrdID, Integer cancelReason, String cancelCReasonTxt) {
         dispatchOrdMapper.updateDispatchOrdCancelReason(dispatchOrdID, cancelReason, cancelCReasonTxt);
     }
+    
+    /**
+     * 鏇存柊璋冨害鍗曞疄闄呭紑濮嬫椂闂�
+     * 
+     * @param dispatchOrdID 璋冨害鍗旾D
+     * @param actualDate 瀹為檯寮�濮嬫椂闂�
+     * @return 褰卞搷琛屾暟
+     */
+    @Override
+    public int updateDispatchOrdActualDate(Long dispatchOrdID, java.util.Date actualDate) {
+        return dispatchOrdMapper.updateDispatchOrdActualDate(dispatchOrdID, actualDate);
+    }
 
 
 } 
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/HospDataSyncServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/HospDataSyncServiceImpl.java
index 4c0eb86..8b5e2a0 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/HospDataSyncServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/HospDataSyncServiceImpl.java
@@ -84,17 +84,23 @@
                     {
                         // 鏇存柊宸叉湁鏁版嵁
                         updateHospData(existingHosp, dto);
+                        // 鐢熸垚鍒嗚瘝
+                        String keywords = tbHospDataService.generateKeywordsForHospital(existingHosp);
+                        existingHosp.setHospKeywords(keywords);
                         tbHospDataService.updateTbHospData(existingHosp);
                         updateCount++;
-                        log.debug("鏇存柊鍖婚櫌: {} (HospID={})", dto.getHospName(), dto.getHospId());
+                        log.debug("鏇存柊鍖婚櫌: {} (HospID={}), 鍒嗚瘝: {}", dto.getHospName(), dto.getHospId(), keywords);
                     }
                     else
                     {
                         // 鎻掑叆鏂版暟鎹�
                         TbHospData newHosp = convertToTbHospData(dto);
+                        // 鐢熸垚鍒嗚瘝
+                        String keywords = tbHospDataService.generateKeywordsForHospital(newHosp);
+                        newHosp.setHospKeywords(keywords);
                         tbHospDataService.insertTbHospData(newHosp);
                         insertCount++;
-                        log.debug("鏂板鍖婚櫌: {} (HospID={})", dto.getHospName(), dto.getHospId());
+                        log.debug("鏂板鍖婚櫌: {} (HospID={}), 鍒嗚瘝: {}", dto.getHospName(), dto.getHospId(), keywords);
                     }
                 }
                 catch (Exception e)
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacySystemSyncServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacySystemSyncServiceImpl.java
index f8a37d7..dc90f20 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacySystemSyncServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacySystemSyncServiceImpl.java
@@ -82,7 +82,10 @@
     private ITaskDispatchSyncService taskDispatchSyncService;
 
 
-
+    @Override
+    public Integer updateDispatchActualTime(Long dispatchOrdId, Date actualTime) {
+        return dispatchOrdService.updateDispatchOrdActualDate(dispatchOrdId, actualTime);
+    }
 
     @Override
     public Long syncEmergencyTaskToLegacy(Long taskId) {
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacyTransferSyncServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacyTransferSyncServiceImpl.java
index 77bca76..c709ddc 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacyTransferSyncServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacyTransferSyncServiceImpl.java
@@ -346,13 +346,13 @@
             createTaskVo.setDeptId(deptId);
 
 
-            int result = sysTaskService.insertTask(createTaskVo,serviceOrdID,dispatchOrdID, serviceOrdNo, taskCreatorId,createUserName, deptId, ServiceOrd_CC_Time, ServiceOrd_CC_Time);
+            Long taskId = sysTaskService.insertTask(createTaskVo,serviceOrdID,dispatchOrdID, serviceOrdNo, taskCreatorId,createUserName, deptId, ServiceOrd_CC_Time, ServiceOrd_CC_Time);
 
-            if (result > 0) {
-//                log.info("杞繍鍗曞悓姝ユ垚鍔�: ServiceOrdID={}, DispatchOrdID={}, 鍒涘缓鐨勪换鍔D={}", serviceOrdID, dispatchOrdID, result);
+            if (taskId != null && taskId > 0) {
+//                log.info("杞繍鍗曞悓姝ユ垚鍔�: ServiceOrdID={}, DispatchOrdID={}, 鍒涘缓鐨勪换鍔D={}", serviceOrdID, dispatchOrdID, taskId);
 
                 try {
-                    notifyTransferOrderByWechat((long) result, serviceOrdID, dispatchOrdID, serviceOrdNo, ServiceOrd_CC_Time, dept, order);
+                    notifyTransferOrderByWechat(taskId, serviceOrdID, dispatchOrdID, serviceOrdNo, ServiceOrd_CC_Time, dept, order);
                 } catch (Exception e) {
                     log.error("杞繍鍗曞悓姝ユ垚鍔熷悗鍙戦�佸井淇¢�氱煡澶辫触: ServiceOrdID={}, DispatchOrdID={}", serviceOrdID, dispatchOrdID, e);
                 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/QyWechatServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/QyWechatServiceImpl.java
index 6d511f2..942496e 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/QyWechatServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/QyWechatServiceImpl.java
@@ -1,5 +1,6 @@
 package com.ruoyi.system.service.impl;
 
+import com.ruoyi.common.config.WechatConfig;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.system.domain.QyWechatArticle;
@@ -38,6 +39,8 @@
     @Autowired
     private SysUserMapper userMapper;
 
+    @Autowired
+    private WechatConfig wechatConfig;
     /**
      * 鍙戦�佷紒涓氬井淇℃秷鎭�
      */
@@ -66,6 +69,11 @@
     }
 
     @Override
+    public boolean sendNotifyMessageWithDefaultAppId(Long userId, String title, String content, String businessUrl){
+        String appId=wechatConfig.getAppId();
+        return sendNotifyMessage(userId,title,content,appId,businessUrl);
+    }
+    @Override
     public boolean sendNotifyMessage(Long userId, String title, String content, String appId, String businessUrl) {
         try {
             // 妫�鏌ユ湇鍔℃槸鍚﹀惎鐢�
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskServiceImpl.java
index 60aae88..b0141f1 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskServiceImpl.java
@@ -252,14 +252,14 @@
      * @return 缁撴灉
      */
     @Override
-    public int insertSysTask(TaskCreateVO createVO) {
+    public Long insertSysTask(TaskCreateVO createVO) {
     // 鑾峰彇褰撳墠鐢ㄦ埛鍚嶅拰鐢ㄦ埛ID
         String username = SecurityUtils.getUsername();
         Long userId = SecurityUtils.getUserId();
     // 鏍¢獙鐢ㄦ埛ID鏄惁涓虹┖鎴栦负0
         if(userId==null || userId==0){
             log.error("insertSysTask 鐢ㄦ埛ID涓虹┖ userName:{}",username);
-            return 0;
+            return 0L;
         }
         SysTask task = new SysTask();
     // 鍒涘缓鏂扮殑浠诲姟瀵硅薄
@@ -347,7 +347,7 @@
             }).start();
         }
         
-        return result;
+        return result > 0 ? task.getTaskId() : 0L;
     }
 
     /**
@@ -361,7 +361,7 @@
      * @return 缁撴灉
      */
     @Override
-    public int insertTask(TaskCreateVO createVO,Long serviceOrderId,Long dispatchOrderId, String serviceOrdNo, Long userId,String userName, Long deptId, Date createTime, Date updateTime) {
+    public Long insertTask(TaskCreateVO createVO,Long serviceOrderId,Long dispatchOrderId, String serviceOrdNo, Long userId,String userName, Long deptId, Date createTime, Date updateTime) {
         SysTask task = new SysTask();
         if(createVO.getTaskCode()!=null){
             task.setTaskCode(createVO.getTaskCode());
@@ -455,7 +455,7 @@
             this.sendEmeryTaskProcess(task, dispatchOrderId);
         }
         
-        return result;
+        return result > 0 ? task.getTaskId() : 0L;
     }
 
     private void sendTaskAssigneeEvent(TaskCreateVO createVO,SysTask task,Long userId,String userName){
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskStatusPushServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskStatusPushServiceImpl.java
index 9d495d9..af792e6 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskStatusPushServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskStatusPushServiceImpl.java
@@ -134,6 +134,27 @@
                     cancelDispatch(emergency.getLegacyDispatchOrdId(), emergency.getCancelReason(), emergency.getCancelBy());
                 }
             }
+            
+            // 鍒ゆ柇鏄惁闇�瑕佹洿鏂板疄闄呭紑濮嬫椂闂达細浠庡緟澶勭悊杞埌鍏朵粬鐘舵�侊紙闄ゅ彇娑堝锛�
+            if ( targetStatusCode != 10 && task.getActualStartTime() != null) {
+                try {
+                    int rows = dispatchOrdService.updateDispatchOrdActualDate(
+                        emergency.getLegacyDispatchOrdId(), 
+                        task.getActualStartTime());
+                    if (rows > 0) {
+                        log.info("銆愭柊鎺ㄦ棫銆戞洿鏂板疄闄呭紑濮嬫椂闂存垚鍔燂紝浠诲姟ID: {}, DispatchOrdID: {}, 瀹為檯寮�濮嬫椂闂�: {}",
+                            taskId, emergency.getLegacyDispatchOrdId(), task.getActualStartTime());
+                    } else {
+                        log.warn("銆愭柊鎺ㄦ棫銆戞洿鏂板疄闄呭紑濮嬫椂闂村け璐ワ紝鏈壘鍒板搴旇皟搴﹀崟锛孌ispatchOrdID: {}", 
+                            emergency.getLegacyDispatchOrdId());
+                    }
+                } catch (Exception e) {
+                    log.error("銆愭柊鎺ㄦ棫銆戞洿鏂板疄闄呭紑濮嬫椂闂村紓甯革紝DispatchOrdID: {}", 
+                        emergency.getLegacyDispatchOrdId(), e);
+                    // 涓嶆姏鍑哄紓甯革紝缁х画鎵ц鐘舵�佹帹閫�
+                }
+            }
+            
             // 鎺ㄩ�佺姸鎬佸埌鏃х郴缁�
             boolean result = updateLegacyTaskStatus(emergency.getLegacyDispatchOrdId(), targetStatusCode);
             
@@ -174,7 +195,7 @@
         
         try {
             int totalSuccessCount = 0;
-            int pageSize = 10; // 姣忛〉10鏉�
+            int pageSize = 5; // 姣忛〉10鏉�
             int offset = 0;
             
             while (true) {
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskStatusSyncServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskStatusSyncServiceImpl.java
index 80db40c..e30517b 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskStatusSyncServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskStatusSyncServiceImpl.java
@@ -231,7 +231,7 @@
             
             // 妫�鏌ョ姸鎬佹槸鍚﹀彉鍖�
             if (newStatus.getCode().equals(task.getTaskStatus())) {
-                log.debug("浠诲姟鐘舵�佹湭鍙樺寲锛屼换鍔D: {}, 褰撳墠鐘舵��: {}", taskId, newStatus.getInfo());
+                log.debug("鍙樺寲锛屼换鍔D: {}, 褰撳墠鐘舵��: {}", taskId, newStatus.getInfo());
                 return true;
             }
             
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbHospDataServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbHospDataServiceImpl.java
index 3f19ae7..5fa0363 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbHospDataServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbHospDataServiceImpl.java
@@ -1,8 +1,11 @@
 package com.ruoyi.system.service.impl;
 
+import com.ruoyi.common.utils.HospitalTokenizerUtil;
 import com.ruoyi.system.domain.TbHospData;
 import com.ruoyi.system.mapper.TbHospDataMapper;
 import com.ruoyi.system.service.ITbHospDataService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -16,6 +19,8 @@
 @Service
 public class TbHospDataServiceImpl implements ITbHospDataService
 {
+    private static final Logger logger = LoggerFactory.getLogger(TbHospDataServiceImpl.class);
+    
     @Autowired
     private TbHospDataMapper tbHospDataMapper;
 
@@ -102,4 +107,63 @@
     {
         return tbHospDataMapper.deleteTbHospDataById(hospId);
     }
+
+    /**
+     * 鎵归噺鐢熸垚骞舵洿鏂版墍鏈夊尰闄㈢殑鍒嗚瘝
+     * 渚涘尰闄㈠悓姝ユ椂璋冪敤
+     * 
+     * @return 鏇存柊鐨勫尰闄㈡暟閲�
+     */
+    @Override
+    public int generateAllHospitalKeywords()
+    {
+        logger.info("寮�濮嬫壒閲忕敓鎴愬尰闄㈠垎璇�...");
+        
+        // 鏌ヨ鎵�鏈夋甯哥姸鎬佺殑鍖婚櫌
+        TbHospData query = new TbHospData();
+        query.setStatus("0");
+        List<TbHospData> hospitalList = tbHospDataMapper.selectTbHospDataList(query);
+        
+        logger.info("鏌ヨ鍒� {} 涓尰闄㈤渶瑕佺敓鎴愬垎璇�", hospitalList.size());
+        
+        int updateCount = 0;
+        for (TbHospData hospital : hospitalList) {
+            try {
+                // 鐢熸垚鍒嗚瘝
+                String keywords = generateKeywordsForHospital(hospital);
+                hospital.setHospKeywords(keywords);
+                
+                // 鏇存柊鏁版嵁搴�
+                int result = tbHospDataMapper.updateTbHospData(hospital);
+                if (result > 0) {
+                    updateCount++;
+                }
+            } catch (Exception e) {
+                logger.error("鐢熸垚鍖婚櫌鍒嗚瘝澶辫触: hospId={}, hospName={}", 
+                    hospital.getHospId(), hospital.getHospName(), e);
+            }
+        }
+        
+        logger.info("鍖婚櫌鍒嗚瘝鐢熸垚瀹屾垚锛屾洿鏂颁簡 {} 涓尰闄�", updateCount);
+        return updateCount;
+    }
+
+    /**
+     * 涓哄崟涓尰闄㈢敓鎴愬垎璇�
+     * 
+     * @param tbHospData 鍖婚櫌鏁版嵁
+     * @return 鐢熸垚鐨勫垎璇�
+     */
+    @Override
+    public String generateKeywordsForHospital(TbHospData tbHospData)
+    {
+        return HospitalTokenizerUtil.tokenize(
+            tbHospData.getHospName(),
+            tbHospData.getHospShort(),
+            tbHospData.getHopsProvince(),
+            tbHospData.getHopsCity(),
+            tbHospData.getHopsArea(),
+            tbHospData.getHospAddress()
+        );
+    }
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleAbnormalAlertServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleAbnormalAlertServiceImpl.java
new file mode 100644
index 0000000..2eb389b
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleAbnormalAlertServiceImpl.java
@@ -0,0 +1,173 @@
+package com.ruoyi.system.service.impl;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.system.mapper.VehicleAbnormalAlertMapper;
+import com.ruoyi.system.domain.VehicleAbnormalAlert;
+import com.ruoyi.system.service.IVehicleAbnormalAlertService;
+
+/**
+ * 杞﹁締寮傚父鍛婅Service涓氬姟灞傚鐞�
+ * 
+ * @author ruoyi
+ */
+@Service
+public class VehicleAbnormalAlertServiceImpl implements IVehicleAbnormalAlertService {
+    
+    @Autowired
+    private VehicleAbnormalAlertMapper vehicleAbnormalAlertMapper;
+
+    /**
+     * 鏌ヨ杞﹁締寮傚父鍛婅
+     * 
+     * @param alertId 杞﹁締寮傚父鍛婅涓婚敭
+     * @return 杞﹁締寮傚父鍛婅
+     */
+    @Override
+    public VehicleAbnormalAlert selectVehicleAbnormalAlertByAlertId(Long alertId) {
+        return vehicleAbnormalAlertMapper.selectVehicleAbnormalAlertByAlertId(alertId);
+    }
+
+    /**
+     * 鏌ヨ杞﹁締寮傚父鍛婅鍒楄〃
+     * 
+     * @param vehicleAbnormalAlert 杞﹁締寮傚父鍛婅
+     * @return 杞﹁締寮傚父鍛婅
+     */
+    @Override
+    public List<VehicleAbnormalAlert> selectVehicleAbnormalAlertList(VehicleAbnormalAlert vehicleAbnormalAlert) {
+        return vehicleAbnormalAlertMapper.selectVehicleAbnormalAlertList(vehicleAbnormalAlert);
+    }
+
+    /**
+     * 鏂板杞﹁締寮傚父鍛婅
+     * 
+     * @param vehicleAbnormalAlert 杞﹁締寮傚父鍛婅
+     * @return 缁撴灉
+     */
+    @Override
+    public int insertVehicleAbnormalAlert(VehicleAbnormalAlert vehicleAbnormalAlert) {
+        vehicleAbnormalAlert.setCreateTime(DateUtils.getNowDate());
+        return vehicleAbnormalAlertMapper.insertVehicleAbnormalAlert(vehicleAbnormalAlert);
+    }
+
+    /**
+     * 淇敼杞﹁締寮傚父鍛婅
+     * 
+     * @param vehicleAbnormalAlert 杞﹁締寮傚父鍛婅
+     * @return 缁撴灉
+     */
+    @Override
+    public int updateVehicleAbnormalAlert(VehicleAbnormalAlert vehicleAbnormalAlert) {
+        vehicleAbnormalAlert.setUpdateTime(DateUtils.getNowDate());
+        return vehicleAbnormalAlertMapper.updateVehicleAbnormalAlert(vehicleAbnormalAlert);
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎杞﹁締寮傚父鍛婅
+     * 
+     * @param alertIds 闇�瑕佸垹闄ょ殑杞﹁締寮傚父鍛婅涓婚敭
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteVehicleAbnormalAlertByAlertIds(Long[] alertIds) {
+        return vehicleAbnormalAlertMapper.deleteVehicleAbnormalAlertByAlertIds(alertIds);
+    }
+
+    /**
+     * 鍒犻櫎杞﹁締寮傚父鍛婅淇℃伅
+     * 
+     * @param alertId 杞﹁締寮傚父鍛婅涓婚敭
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteVehicleAbnormalAlertByAlertId(Long alertId) {
+        return vehicleAbnormalAlertMapper.deleteVehicleAbnormalAlertByAlertId(alertId);
+    }
+
+    /**
+     * 澶勭悊鍛婅
+     * 
+     * @param alertId 鍛婅ID
+     * @param handlerId 澶勭悊浜篒D
+     * @param handlerName 澶勭悊浜哄鍚�
+     * @param handleRemark 澶勭悊澶囨敞
+     * @return 缁撴灉
+     */
+    @Override
+    public int handleAlert(Long alertId, Long handlerId, String handlerName, String handleRemark) {
+        VehicleAbnormalAlert alert = new VehicleAbnormalAlert();
+        alert.setAlertId(alertId);
+        alert.setStatus("1"); // 宸插鐞�
+        alert.setHandlerId(handlerId);
+        alert.setHandlerName(handlerName);
+        alert.setHandleTime(new Date());
+        alert.setHandleRemark(handleRemark);
+        
+        return updateVehicleAbnormalAlert(alert);
+    }
+
+    /**
+     * 鎵归噺澶勭悊鍛婅
+     * 
+     * @param alertIds 鍛婅ID鍒楄〃
+     * @param handlerId 澶勭悊浜篒D
+     * @param handlerName 澶勭悊浜哄鍚�
+     * @param handleRemark 澶勭悊澶囨敞
+     * @return 缁撴灉
+     */
+    @Override
+    public int batchHandleAlert(Long[] alertIds, Long handlerId, String handlerName, String handleRemark) {
+        return vehicleAbnormalAlertMapper.batchHandleAlert(alertIds, handlerId, handlerName, handleRemark);
+    }
+
+    /**
+     * 妫�鏌ュ苟鍒涘缓杞﹁締寮傚父鍛婅
+     * 
+     * @param vehicleId 杞﹁締ID
+     * @param vehicleNo 杞︾墝鍙�
+     * @param mileage 杩愯鍏噷鏁�
+     * @param startTime 寮�濮嬫椂闂�
+     * @param endTime 缁撴潫鏃堕棿
+     * @param deptId 閮ㄩ棬ID
+     * @param deptName 閮ㄩ棬鍚嶇О
+     * @return 鏄惁鍒涘缓鎴愬姛
+     */
+    @Override
+    public boolean checkAndCreateAlert(Long vehicleId, String vehicleNo, BigDecimal mileage, 
+                                       Date startTime, Date endTime, Long deptId, String deptName) {
+        try {
+            // 鑾峰彇褰撴棩鍛婅娆℃暟
+            Date today = DateUtils.parseDate(DateUtils.getDate());
+            int todayCount = vehicleAbnormalAlertMapper.selectDailyAlertCount(vehicleId, today);
+            
+            // 鍒涘缓鍛婅璁板綍
+            VehicleAbnormalAlert alert = new VehicleAbnormalAlert();
+            alert.setVehicleId(vehicleId);
+            alert.setVehicleNo(vehicleNo);
+            alert.setAlertDate(today);
+            alert.setAlertTime(new Date());
+            alert.setMileage(mileage);
+            alert.setAlertType("NO_TASK_MILEAGE");
+            alert.setAlertReason(String.format("杞﹁締鍦ㄦ棤浠诲姟鐘舵�佷笅杩愯浜� %.2f 鍏噷", mileage));
+            alert.setStartTime(startTime);
+            alert.setEndTime(endTime);
+            alert.setAlertCount(todayCount + 1);
+            alert.setStatus("0"); // 鏈鐞�
+            alert.setNotifyStatus("0"); // 鏈彂閫�
+            alert.setDeptId(deptId);
+            alert.setDeptName(deptName);
+            
+            int result = insertVehicleAbnormalAlert(alert);
+            return result > 0;
+            
+        } catch (Exception e) {
+            throw new RuntimeException("鍒涘缓鍛婅璁板綍澶辫触: " + e.getMessage(), e);
+        }
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleAlertConfigServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleAlertConfigServiceImpl.java
new file mode 100644
index 0000000..efbe423
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleAlertConfigServiceImpl.java
@@ -0,0 +1,109 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.List;
+import com.ruoyi.common.utils.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.system.mapper.VehicleAlertConfigMapper;
+import com.ruoyi.system.domain.VehicleAlertConfig;
+import com.ruoyi.system.service.IVehicleAlertConfigService;
+
+/**
+ * 杞﹁締鍛婅閰嶇疆Service涓氬姟灞傚鐞�
+ * 
+ * @author ruoyi
+ * @date 2026-01-12
+ */
+@Service
+public class VehicleAlertConfigServiceImpl implements IVehicleAlertConfigService 
+{
+    @Autowired
+    private VehicleAlertConfigMapper vehicleAlertConfigMapper;
+
+    /**
+     * 鏌ヨ杞﹁締鍛婅閰嶇疆
+     * 
+     * @param configId 杞﹁締鍛婅閰嶇疆涓婚敭
+     * @return 杞﹁締鍛婅閰嶇疆
+     */
+    @Override
+    public VehicleAlertConfig selectVehicleAlertConfigByConfigId(Long configId)
+    {
+        return vehicleAlertConfigMapper.selectVehicleAlertConfigByConfigId(configId);
+    }
+
+    /**
+     * 鏌ヨ杞﹁締鍛婅閰嶇疆鍒楄〃
+     * 
+     * @param vehicleAlertConfig 杞﹁締鍛婅閰嶇疆
+     * @return 杞﹁締鍛婅閰嶇疆
+     */
+    @Override
+    public List<VehicleAlertConfig> selectVehicleAlertConfigList(VehicleAlertConfig vehicleAlertConfig)
+    {
+        return vehicleAlertConfigMapper.selectVehicleAlertConfigList(vehicleAlertConfig);
+    }
+
+    /**
+     * 鑾峰彇杞﹁締鐨勫憡璀﹂厤缃紙浼樺厛绾э細杞﹁締 > 閮ㄩ棬 > 鍏ㄥ眬锛�
+     * 
+     * @param vehicleId 杞﹁締ID
+     * @param deptId 閮ㄩ棬ID
+     * @return 杞﹁締鍛婅閰嶇疆
+     */
+    @Override
+    public VehicleAlertConfig getConfigByVehicle(Long vehicleId, Long deptId)
+    {
+        return vehicleAlertConfigMapper.selectConfigByVehicle(vehicleId, deptId);
+    }
+
+    /**
+     * 鏂板杞﹁締鍛婅閰嶇疆
+     * 
+     * @param vehicleAlertConfig 杞﹁締鍛婅閰嶇疆
+     * @return 缁撴灉
+     */
+    @Override
+    public int insertVehicleAlertConfig(VehicleAlertConfig vehicleAlertConfig)
+    {
+        vehicleAlertConfig.setCreateTime(DateUtils.getNowDate());
+        return vehicleAlertConfigMapper.insertVehicleAlertConfig(vehicleAlertConfig);
+    }
+
+    /**
+     * 淇敼杞﹁締鍛婅閰嶇疆
+     * 
+     * @param vehicleAlertConfig 杞﹁締鍛婅閰嶇疆
+     * @return 缁撴灉
+     */
+    @Override
+    public int updateVehicleAlertConfig(VehicleAlertConfig vehicleAlertConfig)
+    {
+        vehicleAlertConfig.setUpdateTime(DateUtils.getNowDate());
+        return vehicleAlertConfigMapper.updateVehicleAlertConfig(vehicleAlertConfig);
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎杞﹁締鍛婅閰嶇疆
+     * 
+     * @param configIds 闇�瑕佸垹闄ょ殑杞﹁締鍛婅閰嶇疆涓婚敭
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteVehicleAlertConfigByConfigIds(Long[] configIds)
+    {
+        return vehicleAlertConfigMapper.deleteVehicleAlertConfigByConfigIds(configIds);
+    }
+
+    /**
+     * 鍒犻櫎杞﹁締鍛婅閰嶇疆淇℃伅
+     * 
+     * @param configId 杞﹁締鍛婅閰嶇疆涓婚敭
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteVehicleAlertConfigByConfigId(Long configId)
+    {
+        return vehicleAlertConfigMapper.deleteVehicleAlertConfigByConfigId(configId);
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleGpsSegmentMileageServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleGpsSegmentMileageServiceImpl.java
index a534c6e..8c20c0a 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleGpsSegmentMileageServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleGpsSegmentMileageServiceImpl.java
@@ -254,9 +254,8 @@
                     }
                 }
                 
-                // 姣忔壒娆$粨鏉熷悗锛屼富鍔ㄥ缓璁瓽C
+                // 姣忔壒娆$粨鏉熷悗锛屼富鍔ㄥ缓璁瓽C锛堜笉闇�瑕佹樉寮忔竻绌哄紩鐢紝灞�閮ㄥ彉閲忎細鑷姩閲婃斁锛�
                 if (batchEnd < vehicleIds.size()) {
-                    uncalculatedGps = null; // 鏄惧紡娓呯┖寮曠敤
                     System.gc();
                     logger.debug("琛ュ伩璁$畻鎵规 {}-{} 瀹屾垚锛屽凡寤鸿JVM鍥炴敹鍐呭瓨", batchStart + 1, batchEnd);
                 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/AliOCRUtil.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/AliOCRUtil.java
new file mode 100644
index 0000000..c5c05d2
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/AliOCRUtil.java
@@ -0,0 +1,457 @@
+package com.ruoyi.system.utils;
+
+import com.aliyun.ocr_api20210707.Client;
+import com.aliyun.ocr_api20210707.models.RecognizeAllTextRequest;
+import com.aliyun.ocr_api20210707.models.RecognizeAllTextResponse;
+import com.aliyun.ocr_api20210707.models.RecognizeHandwritingRequest;
+import com.aliyun.ocr_api20210707.models.RecognizeHandwritingResponse;
+import com.aliyun.tea.TeaException;
+import com.aliyun.teaopenapi.models.Config;
+import com.aliyun.teautil.models.RuntimeOptions;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.system.config.OCRConfig;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 闃块噷浜慜CR宸ュ叿绫� - 閫氱敤鏂囧瓧璇嗗埆
+ * 鏀寔澶氱璇嗗埆绫诲瀷锛氶�氱敤鏂囧瓧銆佸彂绁ㄣ�佽韩浠借瘉銆佹墜鍐欎綋绛�
+ * 浣跨敤 ocr-api20210707 SDK
+ * 
+ * 浣跨敤绀轰緥锛�
+ * // 閫氱敤鏂囧瓧璇嗗埆
+ * JSONObject result = AliOCRUtil.recognizeGeneral("https://example.com/image.jpg");
+ * 
+ * // 鏈湴鍥剧墖璇嗗埆
+ * File imageFile = new File("path/to/image.jpg");
+ * JSONObject result = AliOCRUtil.recognizeGeneral(imageFile);
+ * 
+ * // 鎵嬪啓浣撹瘑鍒�
+ * JSONObject result = AliOCRUtil.recognizeHandwriting(imageFile);
+ * 
+ * // 鎻愬彇鍏抽敭瀛楁
+ * Map<String, String> fields = AliOCRUtil.extractTargetFields(result);
+ */
+@Component
+public class AliOCRUtil {
+    
+    private static final Logger log = LoggerFactory.getLogger(AliOCRUtil.class);
+
+    private static OCRConfig staticOcrConfig;
+
+    @Autowired
+    public void setOcrConfig(OCRConfig ocrConfig) {
+        AliOCRUtil.staticOcrConfig = ocrConfig;
+    }
+
+
+    // OCR API 绔偣
+    private static final String ENDPOINT = "ocr-api.cn-hangzhou.aliyuncs.com";
+
+    /**
+     * 鍒涘缓闃块噷浜慜CR瀹㈡埛绔�
+     * @return OCR瀹㈡埛绔疄渚�
+     * @throws Exception 鍒涘缓澶辫触鏃舵姏鍑哄紓甯�
+     */
+    private static Client createClient() throws Exception {
+        Config config = new Config()
+                .setAccessKeyId(staticOcrConfig.getAccessKeyId())
+                .setAccessKeySecret(staticOcrConfig.getAccessKeySecret());
+        config.endpoint = ENDPOINT;
+        // 璁剧疆杩炴帴瓒呮椂鏃堕棿
+        config.connectTimeout = 10000; // 10绉�
+        config.readTimeout = 30000; // 30绉�
+        return new Client(config);
+    }
+
+    /**
+     * 鍒涘缓闃块噷浜慜CR瀹㈡埛绔紙鏀寔澶栭儴浼犲叆AK/SK锛�
+     * @param accessKeyId 闃块噷浜慉ccessKey ID
+     * @param accessKeySecret 闃块噷浜慉ccessKey Secret
+     * @return OCR瀹㈡埛绔疄渚�
+     * @throws Exception 鍒涘缓澶辫触鏃舵姏鍑哄紓甯�
+     */
+    public static Client createClient(String accessKeyId, String accessKeySecret) throws Exception {
+        Config config = new Config()
+                .setAccessKeyId(accessKeyId)
+                .setAccessKeySecret(accessKeySecret);
+        config.endpoint = ENDPOINT;
+        // 璁剧疆杩炴帴瓒呮椂鏃堕棿
+        config.connectTimeout = 10000; // 10绉�
+        config.readTimeout = 30000; // 30绉�
+        return new Client(config);
+    }
+
+    /**
+     * 灏嗗浘鐗囨枃浠惰浆涓築ase64缂栫爜
+     * @param imageFile 鍥剧墖鏂囦欢
+     * @return Base64缂栫爜瀛楃涓�
+     * @throws IOException 璇诲彇鏂囦欢澶辫触
+     */
+    public static String imageToBase64(File imageFile) throws IOException {
+        try (FileInputStream fis = new FileInputStream(imageFile)) {
+            byte[] buffer = new byte[(int) imageFile.length()];
+            fis.read(buffer);
+            return Base64.encodeBase64String(buffer);
+        }
+    }
+
+    /**
+     * 璋冪敤闃块噷浜戦�氱敤鏂囧瓧璇嗗埆OCR鎺ュ彛锛堜娇鐢ㄥ浘鐗嘦RL锛�
+     * @param imageUrl 鍥剧墖URL鍦板潃
+     * @param type 璇嗗埆绫诲瀷锛堝锛�"Invoice"-鍙戠エ, "IdCard"-韬唤璇�, "General"-閫氱敤, "HandWriting"-鎵嬪啓浣擄級
+     * @return OCR璇嗗埆缁撴灉锛圝SON鏍煎紡锛�
+     */
+    public static JSONObject recognizeTextByUrl(String imageUrl, String type) {
+        try {
+            Client client = createClient();
+            RecognizeAllTextRequest request = new RecognizeAllTextRequest()
+                    .setUrl(imageUrl)
+                    .setType(type);
+            
+            RuntimeOptions runtime = new RuntimeOptions();
+            RecognizeAllTextResponse response = client.recognizeAllTextWithOptions(request, runtime);
+            
+            // 灏嗗搷搴旇浆涓篔SONObject
+            String responseJson = com.aliyun.teautil.Common.toJSONString(response.body.data);
+            JSONObject result = JSON.parseObject(responseJson);
+            result.put("success", true);
+            
+            log.info("OCR璇嗗埆鎴愬姛锛岀被鍨�: {}, URL: {}", type, imageUrl);
+            return result;
+            
+        } catch (TeaException error) {
+            log.error("OCR璇嗗埆澶辫触锛圱eaException锛�: {}", error.getMessage(), error);
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", error.getMessage());
+            if (error.getData() != null) {
+                errorResult.put("recommend", error.getData().get("Recommend"));
+            }
+            return errorResult;
+            
+        } catch (Exception error) {
+            log.error("OCR璇嗗埆澶辫触锛圗xception锛�: {}", error.getMessage(), error);
+            
+            // 鏋勫缓璇︾粏鐨勯敊璇俊鎭�
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            
+            // 鍒ゆ柇閿欒绫诲瀷
+            String errorMsg = error.getMessage();
+            if (errorMsg != null && errorMsg.contains("ocr-api.cn-hangzhou.aliyuncs.com")) {
+                errorResult.put("error", "缃戠粶杩炴帴澶辫触锛氭棤娉曡闂樋閲屼簯OCR鏈嶅姟");
+                errorResult.put("detail", "璇锋鏌ワ細1) 缃戠粶杩炴帴鏄惁姝e父 2) DNS瑙f瀽鏄惁鍙敤 3) 闃茬伀澧欒缃� 4) 浠g悊閰嶇疆");
+                errorResult.put("endpoint", ENDPOINT);
+            } else if (error instanceof java.io.FileNotFoundException) {
+                errorResult.put("error", "鏂囦欢涓嶅瓨鍦�: " + imageUrl);
+            } else if (error instanceof java.io.IOException) {
+                errorResult.put("error", "鏂囦欢璇诲彇澶辫触: " + errorMsg);
+            } else {
+                errorResult.put("error", errorMsg != null ? errorMsg : "鏈煡閿欒");
+            }
+            
+            return errorResult;
+        }
+    }
+
+    /**
+     * 璋冪敤闃块噷浜戦�氱敤鏂囧瓧璇嗗埆OCR鎺ュ彛锛堜娇鐢ㄦ湰鍦板浘鐗囨枃浠讹級
+     * @param imageFile 鍥剧墖鏂囦欢
+     * @param type 璇嗗埆绫诲瀷锛堝锛�"Invoice"-鍙戠エ, "IdCard"-韬唤璇�, "General"-閫氱敤, "HandWriting"-鎵嬪啓浣擄級
+     * @return OCR璇嗗埆缁撴灉锛圝SON鏍煎紡锛�
+     */
+    public static JSONObject recognizeTextByFile(File imageFile, String type) {
+        FileInputStream fis = null;
+        try {
+            // 鐩存帴璇诲彇鍥剧墖鏂囦欢涓哄瓧鑺傛祦
+            fis = new FileInputStream(imageFile);
+            
+            Client client = createClient();
+            RecognizeAllTextRequest request = new RecognizeAllTextRequest()
+                    .setBody(fis)  // 鐩存帴浼犲叆鏂囦欢娴�
+                    .setType(type);
+            JSONObject result;
+            RuntimeOptions runtime = new RuntimeOptions();
+            if(type.equals("HandWriting")){//澶勭悊鎵嬪啓{
+                RecognizeHandwritingRequest handwritingRequest=new RecognizeHandwritingRequest();
+                handwritingRequest.setBody(fis);
+                handwritingRequest.setNeedSortPage(true);//浠庝笂鍒颁笅锛屼粠宸﹀埌鍙�
+                RecognizeHandwritingResponse response = client.recognizeHandwriting(handwritingRequest);
+                String responseJson = com.aliyun.teautil.Common.toJSONString(response.body.data);
+                 result = JSON.parseObject(responseJson);
+                result.put("success", true);
+            }else {
+                RecognizeAllTextResponse response = client.recognizeAllTextWithOptions(request, runtime);
+
+                // 灏嗗搷搴旇浆涓篔SONObject
+                String responseJson = com.aliyun.teautil.Common.toJSONString(response.body.data);
+                 result = JSON.parseObject(responseJson);
+                result.put("success", true);
+            }
+            log.info("OCR璇嗗埆鎴愬姛锛岀被鍨�: {}, 鏂囦欢: {} 缁撴灉:{}", type, imageFile.getName(),result);
+            return result;
+            
+        } catch (TeaException error) {
+            log.error("OCR璇嗗埆澶辫触锛圱eaException锛�: {}", error.getMessage(), error);
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", error.getMessage());
+            if (error.getData() != null) {
+                errorResult.put("recommend", error.getData().get("Recommend"));
+            }
+            return errorResult;
+            
+        } catch (Exception error) {
+            log.error("OCR璇嗗埆澶辫触锛圗xception锛�: {}", error.getMessage(), error);
+            
+            // 鏋勫缓璇︾粏鐨勯敊璇俊鎭�
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            
+            // 鍒ゆ柇閿欒绫诲瀷
+            String errorMsg = error.getMessage();
+            if (errorMsg != null && errorMsg.contains("ocr-api.cn-hangzhou.aliyuncs.com")) {
+                errorResult.put("error", "缃戠粶杩炴帴澶辫触锛氭棤娉曡闂樋閲屼簯OCR鏈嶅姟");
+                errorResult.put("detail", "璇锋鏌ワ細1) 缃戠粶杩炴帴鏄惁姝e父 2) DNS瑙f瀽鏄惁鍙敤 3) 闃茬伀澧欒缃� 4) 浠g悊閰嶇疆");
+                errorResult.put("endpoint", ENDPOINT);
+            } else if (error instanceof java.io.FileNotFoundException) {
+                errorResult.put("error", "鏂囦欢涓嶅瓨鍦�: " + imageFile.getAbsolutePath());
+            } else if (error instanceof java.io.IOException) {
+                errorResult.put("error", "鏂囦欢璇诲彇澶辫触: " + errorMsg);
+            } else {
+                errorResult.put("error", errorMsg != null ? errorMsg : "鏈煡閿欒");
+            }
+            
+            return errorResult;
+        } finally {
+            // 鍏抽棴鏂囦欢娴�
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (IOException e) {
+                    log.error("鍏抽棴鏂囦欢娴佸け璐�", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * 璇嗗埆绫诲瀷鏋氫妇
+     */
+    public enum OcrType {
+        GENERAL("General", "閫氱敤鏂囧瓧璇嗗埆"),
+        INVOICE("Invoice", "鍙戠エ璇嗗埆"),
+        IDCARD("IdCard", "韬唤璇佽瘑鍒�"),
+        HANDWRITING("HandWriting", "鎵嬪啓浣撹瘑鍒�");
+
+        private final String code;
+        private final String desc;
+
+        OcrType(String code, String desc) {
+            this.code = code;
+            this.desc = desc;
+        }
+
+        public String getCode() {
+            return code;
+        }
+
+        public String getDesc() {
+            return desc;
+        }
+    }
+
+    /**
+     * 閫氱敤鏂囧瓧璇嗗埆 - 鎵嬪啓浣撹瘑鍒�
+     * @param imageUrl 鍥剧墖URL
+     * @return OCR璇嗗埆缁撴灉
+     */
+    public static JSONObject recognizeHandwriting(String imageUrl) {
+        return recognizeTextByUrl(imageUrl, "HandWriting");
+    }
+
+    /**
+     * 閫氱敤鏂囧瓧璇嗗埆 - 鎵嬪啓浣撹瘑鍒� - 鏈湴鏂囦欢
+     * @param imageFile 鍥剧墖鏂囦欢
+     * @return OCR璇嗗埆缁撴灉
+     */
+    public static JSONObject recognizeHandwriting(File imageFile) {
+        return recognizeTextByFile(imageFile, "HandWriting");
+    }
+
+    /**
+     * 閫氱敤鏂囧瓧璇嗗埆
+     * @param imageUrl 鍥剧墖URL
+     * @return OCR璇嗗埆缁撴灉
+     */
+    public static JSONObject recognizeGeneral(String imageUrl) {
+        return recognizeTextByUrl(imageUrl, "General");
+    }
+
+    /**
+     * 閫氱敤鏂囧瓧璇嗗埆 - 鏈湴鏂囦欢
+     * @param imageFile 鍥剧墖鏂囦欢
+     * @return OCR璇嗗埆缁撴灉
+     */
+    public static JSONObject recognizeGeneral(File imageFile) {
+        return recognizeTextByFile(imageFile, "General");
+    }
+
+    /**
+     * 璇嗗埆閫氱敤绁ㄦ嵁锛堝彂绁ㄣ�佹敹鎹瓑锛�
+     * @param imageUrl 鍥剧墖URL
+     * @return OCR璇嗗埆缁撴灉
+     */
+    public static JSONObject recognizeInvoice(String imageUrl) {
+        return recognizeTextByUrl(imageUrl, "Invoice");
+    }
+
+    /**
+     * 璇嗗埆閫氱敤绁ㄦ嵁锛堝彂绁ㄣ�佹敹鎹瓑锛�- 鏈湴鏂囦欢
+     * @param imageFile 鍥剧墖鏂囦欢
+     * @return OCR璇嗗埆缁撴灉
+     */
+    public static JSONObject recognizeInvoice(File imageFile) {
+        return recognizeTextByFile(imageFile, "Invoice");
+    }
+
+    /**
+     * 璇嗗埆韬唤璇�
+     * @param imageUrl 鍥剧墖URL
+     * @return OCR璇嗗埆缁撴灉
+     */
+    public static JSONObject recognizeIdCard(String imageUrl) {
+        return recognizeTextByUrl(imageUrl, "IdCard");
+    }
+
+    /**
+     * 璇嗗埆韬唤璇� - 鏈湴鏂囦欢
+     * @param imageFile 鍥剧墖鏂囦欢
+     * @return OCR璇嗗埆缁撴灉
+     */
+    public static JSONObject recognizeIdCard(File imageFile) {
+        return recognizeTextByFile(imageFile, "IdCard");
+    }
+
+    /**
+     * 浠嶰CR缁撴灉涓彁鍙栫洰鏍囧瓧娈碉紙閲戦銆佹棩鏈熴�佸娉ㄧ瓑锛�
+     * @param ocrResult OCR璇嗗埆鐨勫師濮嬬粨鏋�
+     * @return 鎻愬彇鍚庣殑鐩爣瀛楁
+     */
+    public static Map<String, String> extractTargetFields(JSONObject ocrResult) {
+        Map<String, String> extracted = new HashMap<>();
+
+        // 鏍¢獙OCR缁撴灉鏄惁鏈夋晥
+        if (!ocrResult.containsKey("success") || !ocrResult.getBooleanValue("success")) {
+            extracted.put("error", ocrResult.getString("error"));
+            return extracted;
+        }
+
+        // 鑾峰彇璇嗗埆鐨勬枃瀛楀唴瀹�
+        if (!ocrResult.containsKey("content")) {
+            extracted.put("error", "OCR璇嗗埆缁撴灉涓虹┖");
+            return extracted;
+        }
+
+        String content = ocrResult.getString("content");
+        
+        // 濡傛灉鏈夌粨鏋勫寲鐨刾rism_wordsInfo瀛楁锛屼紭鍏堜娇鐢�
+        if (ocrResult.containsKey("prism_wordsInfo")) {
+            JSONArray wordsInfo = ocrResult.getJSONArray("prism_wordsInfo");
+            
+            // 鎻愬彇閲戦锛堝尮閰嶅寘鍚�"閲戦""鍚堣""楼"鐨勫瓧娈碉級
+            for (int i = 0; i < wordsInfo.size(); i++) {
+                JSONObject word = wordsInfo.getJSONObject(i);
+                String text = word.getString("word");
+                if (text.contains("閲戦") || text.contains("鍚堣") || text.contains("楼")) {
+                    extracted.put("totalAmount", text);
+                    break;
+                }
+            }
+
+            // 鎻愬彇鏃ユ湡
+            for (int i = 0; i < wordsInfo.size(); i++) {
+                JSONObject word = wordsInfo.getJSONObject(i);
+                String text = word.getString("word");
+                if (text.contains("鏃ユ湡") || text.matches(".*\\d{4}[-/骞碷\\d{1,2}[-/鏈圿\\d{1,2}.*")) {
+                    extracted.put("date", text);
+                    break;
+                }
+            }
+
+            // 鎻愬彇澶囨敞
+            for (int i = 0; i < wordsInfo.size(); i++) {
+                JSONObject word = wordsInfo.getJSONObject(i);
+                String text = word.getString("word");
+                if (text.contains("澶囨敞")) {
+                    extracted.put("remark", text);
+                    break;
+                }
+            }
+        } else {
+            // 浣跨敤鏁翠綋鏂囨湰鍐呭杩涜绠�鍗曟彁鍙�
+            extracted.put("fullText", content);
+        }
+
+        return extracted;
+    }
+
+    /**
+     * 浣跨敤鑷畾涔堿ccessKey杩涜OCR璇嗗埆
+     * @param imageUrl 鍥剧墖URL
+     * @param type 璇嗗埆绫诲瀷
+     * @param accessKeyId AccessKey ID
+     * @param accessKeySecret AccessKey Secret
+     * @return OCR璇嗗埆缁撴灉
+     */
+    public static JSONObject recognizeTextWithCredentials(String imageUrl, String type, 
+                                                          String accessKeyId, String accessKeySecret) {
+        try {
+            Client client = createClient(accessKeyId, accessKeySecret);
+            RecognizeAllTextRequest request = new RecognizeAllTextRequest()
+                    .setUrl(imageUrl)
+                    .setType(type);
+            
+            RuntimeOptions runtime = new RuntimeOptions();
+            RecognizeAllTextResponse response = client.recognizeAllTextWithOptions(request, runtime);
+            
+            String responseJson = com.aliyun.teautil.Common.toJSONString(response.body.data);
+            JSONObject result = JSON.parseObject(responseJson);
+            result.put("success", true);
+            
+            log.info("OCR璇嗗埆鎴愬姛锛堣嚜瀹氫箟AK锛夛紝绫诲瀷: {}", type);
+            return result;
+            
+        } catch (TeaException error) {
+            log.error("OCR璇嗗埆澶辫触锛圱eaException锛�: {}", error.getMessage(), error);
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", error.getMessage());
+            if (error.getData() != null) {
+                errorResult.put("recommend", error.getData().get("Recommend"));
+            }
+            return errorResult;
+            
+        } catch (Exception error) {
+            log.error("OCR璇嗗埆澶辫触锛圗xception锛�: {}", error.getMessage(), error);
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", error.getMessage());
+            return errorResult;
+        }
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/BaiduOCRUtil.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/BaiduOCRUtil.java
new file mode 100644
index 0000000..7513752
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/BaiduOCRUtil.java
@@ -0,0 +1,445 @@
+package com.ruoyi.system.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.baidu.aip.ocr.AipOcr;
+import com.ruoyi.system.config.BaiduOCRConfig;
+
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 鐧惧害OCR宸ュ叿绫�
+ * 浣跨敤鐧惧害AI寮�鏀惧钩鍙扮殑OCR鏈嶅姟杩涜鏂囧瓧璇嗗埆
+ * 鏀寔閫氱敤鏂囧瓧璇嗗埆銆佹墜鍐欎綋璇嗗埆绛夊绉嶈瘑鍒被鍨�
+ * 
+ * 浣跨敤绀轰緥锛�
+ * // 閫氱敤鏂囧瓧璇嗗埆
+ * JSONObject result = BaiduOCRUtil.generalRecognize("path/to/image.jpg");
+ * 
+ * // 鎵嬪啓浣撹瘑鍒�
+ * JSONObject result = BaiduOCRUtil.handwritingRecognize("path/to/image.jpg");
+ */
+@Component
+@Slf4j
+public class BaiduOCRUtil {
+
+
+
+    private static BaiduOCRConfig staticBaiduOcrConfig;
+
+    @Autowired
+    public void setBaiduOcrConfig(BaiduOCRConfig baiduOcrConfig) {
+        BaiduOCRUtil.staticBaiduOcrConfig = baiduOcrConfig;
+    }
+
+    /**
+     * 鑾峰彇鐧惧害OCR瀹㈡埛绔疄渚�
+     * @return AipOcr瀹㈡埛绔疄渚�
+     */
+    private static AipOcr getClient() {
+        AipOcr client = new AipOcr(staticBaiduOcrConfig.getAppId(), 
+                                   staticBaiduOcrConfig.getApiKey(), 
+                                   staticBaiduOcrConfig.getSecretKey());
+
+        // 璁剧疆杩炴帴瓒呮椂鏃堕棿鍜宻ocket瓒呮椂鏃堕棿
+        client.setConnectionTimeoutInMillis(2000);
+        client.setSocketTimeoutInMillis(60000);
+
+        return client;
+    }
+
+    /**
+     * 閫氱敤鏂囧瓧璇嗗埆锛堝浘鐗囪矾寰勶級
+     * @param imagePath 鍥剧墖璺緞
+     * @return 璇嗗埆缁撴灉
+     */
+    public static JSONObject generalRecognize(String imagePath) {
+        try {
+            AipOcr client = getClient();
+            
+            // 鍙傛暟涓哄浘鐗囪矾寰�
+            HashMap<String, String> options = new HashMap<String, String>();
+            options.put("language_type", "CHN_ENG"); // 璇嗗埆璇█绫诲瀷
+            options.put("detect_direction", "true"); // 鏄惁妫�娴嬪浘鍍忔湞鍚�
+            options.put("detect_language", "true"); // 鏄惁妫�娴嬭瑷�
+            options.put("probability", "true"); // 鏄惁杩斿洖璇嗗埆缁撴灉涓瘡涓�琛岀殑缃俊搴�
+            
+            org.json.JSONObject res = client.basicGeneral(imagePath, options);
+            log.info("鐧惧害OCR閫氱敤鏂囧瓧璇嗗埆鎴愬姛锛屽浘鐗囪矾寰�: {}", imagePath);
+            
+            JSONObject result = new JSONObject();
+            result.put("success", true);
+            result.put("data", JSON.parseObject(res.toString()));
+            result.put("content", extractContentFromBaiduResult(JSON.parseObject(res.toString())));
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("鐧惧害OCR閫氱敤鏂囧瓧璇嗗埆澶辫触: {}", e.getMessage(), e);
+            
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", e.getMessage());
+            
+            return errorResult;
+        }
+    }
+
+    /**
+     * 閫氱敤鏂囧瓧璇嗗埆锛堟枃浠跺璞★級
+     * @param imageFile 鍥剧墖鏂囦欢瀵硅薄
+     * @return 璇嗗埆缁撴灉
+     */
+    public static JSONObject generalRecognize(File imageFile) {
+        try {
+            AipOcr client = getClient();
+            
+            // 鍙傛暟涓哄浘鐗囨枃浠�
+            HashMap<String, String> options = new HashMap<String, String>();
+            options.put("language_type", "CHN_ENG");
+            options.put("detect_direction", "true");
+            options.put("detect_language", "true");
+            options.put("probability", "true");
+            
+            // 璇诲彇鏂囦欢瀛楄妭鏁扮粍鎴栦娇鐢ㄦ枃浠惰矾寰�
+            org.json.JSONObject res = client.basicGeneral(imageFile.getAbsolutePath(), options);
+            log.info("鐧惧害OCR閫氱敤鏂囧瓧璇嗗埆鎴愬姛锛屾枃浠跺悕: {}", imageFile.getName());
+            
+            JSONObject result = new JSONObject();
+            result.put("success", true);
+            result.put("data", JSON.parseObject(res.toString()));
+            result.put("content", extractContentFromBaiduResult(JSON.parseObject(res.toString())));
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("鐧惧害OCR閫氱敤鏂囧瓧璇嗗埆澶辫触: {}", e.getMessage(), e);
+            
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", e.getMessage());
+            
+            return errorResult;
+        }
+    }
+
+    /**
+     * 閫氱敤鏂囧瓧璇嗗埆锛堝浘鐗囧瓧鑺傛暟缁勶級
+     * @param imageBytes 鍥剧墖瀛楄妭鏁扮粍
+     * @return 璇嗗埆缁撴灉
+     */
+    public static JSONObject generalRecognize(byte[] imageBytes) {
+        try {
+            AipOcr client = getClient();
+            
+            HashMap<String, String> options = new HashMap<String, String>();
+            options.put("language_type", "CHN_ENG");
+            options.put("detect_direction", "true");
+            options.put("detect_language", "true");
+            options.put("probability", "true");
+            
+            org.json.JSONObject res = client.basicGeneral(imageBytes, options);
+            log.info("鐧惧害OCR閫氱敤鏂囧瓧璇嗗埆鎴愬姛锛屽瓧鑺傛暟缁勯暱搴�: {}", imageBytes.length);
+            
+            JSONObject result = new JSONObject();
+            result.put("success", true);
+            result.put("data", JSON.parseObject(res.toString()));
+            result.put("content", extractContentFromBaiduResult(JSON.parseObject(res.toString())));
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("鐧惧害OCR閫氱敤鏂囧瓧璇嗗埆澶辫触: {}", e.getMessage(), e);
+            
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", e.getMessage());
+            
+            return errorResult;
+        }
+    }
+
+    /**
+     * 楂樼簿搴︽枃瀛楄瘑鍒�
+     * @param imagePath 鍥剧墖璺緞
+     * @return 璇嗗埆缁撴灉
+     */
+    public static JSONObject accurateRecognize(String imagePath) {
+        try {
+            AipOcr client = getClient();
+            
+            HashMap<String, String> options = new HashMap<String, String>();
+            options.put("recognize_granularity", "big"); // 鏄惁瀹氫綅鍗曞瓧绗︿綅缃�
+            options.put("language_type", "CHN_ENG");
+            options.put("detect_direction", "true");
+            options.put("detect_language", "true");
+            options.put("vertexes_location", "true"); // 鏄惁杩斿洖鏂囧瓧澶栨帴澶氳竟褰㈤《鐐逛綅缃�
+            options.put("probability", "true");
+            
+            org.json.JSONObject res = client.accurateGeneral(imagePath, options);
+            log.info("鐧惧害OCR楂樼簿搴︽枃瀛楄瘑鍒垚鍔燂紝鍥剧墖璺緞: {}", imagePath);
+            
+            JSONObject result = new JSONObject();
+            result.put("success", true);
+            result.put("data", JSON.parseObject(res.toString()));
+            result.put("content", extractContentFromBaiduResult(JSON.parseObject(res.toString())));
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("鐧惧害OCR楂樼簿搴︽枃瀛楄瘑鍒け璐�: {}", e.getMessage(), e);
+            
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", e.getMessage());
+            
+            return errorResult;
+        }
+    }
+
+    /**
+     * 鎵嬪啓浣撹瘑鍒�
+     * @param imagePath 鍥剧墖璺緞
+     * @return 璇嗗埆缁撴灉
+     */
+    public static JSONObject handwritingRecognize(String imagePath) {
+        try {
+            AipOcr client = getClient();
+            
+            // 鎵嬪啓浣撹瘑鍒弬鏁�
+            HashMap<String, String> options = new HashMap<String, String>();
+            options.put("language_type", "CHN_ENG");
+            
+            org.json.JSONObject res = client.handwriting(imagePath, options);
+            log.info("鐧惧害OCR鎵嬪啓浣撹瘑鍒垚鍔燂紝鍥剧墖璺緞: {}", imagePath);
+            
+            JSONObject result = new JSONObject();
+            result.put("success", true);
+            result.put("data", JSON.parseObject(res.toString()));
+            result.put("content", extractContentFromBaiduResult(JSON.parseObject(res.toString())));
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("鐧惧害OCR鎵嬪啓浣撹瘑鍒け璐�: {}", e.getMessage(), e);
+            
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", e.getMessage());
+            
+            return errorResult;
+        }
+    }
+
+    /**
+     * 鎵嬪啓浣撹瘑鍒紙鏂囦欢瀵硅薄锛�
+     * @param imageFile 鍥剧墖鏂囦欢瀵硅薄
+     * @return 璇嗗埆缁撴灉
+     */
+    public static JSONObject handwritingRecognize(File imageFile) {
+        try {
+            AipOcr client = getClient();
+            
+            HashMap<String, String> options = new HashMap<String, String>();
+            options.put("language_type", "CHN_ENG");
+            
+            org.json.JSONObject res = client.handwriting(imageFile.getAbsolutePath(), options);
+            log.info("鐧惧害OCR鎵嬪啓浣撹瘑鍒垚鍔燂紝鏂囦欢鍚�: {}", imageFile.getName());
+            
+            JSONObject result = new JSONObject();
+            result.put("success", true);
+            result.put("data", JSON.parseObject(res.toString()));
+            result.put("content", extractContentFromBaiduResult(JSON.parseObject(res.toString())));
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("鐧惧害OCR鎵嬪啓浣撹瘑鍒け璐�: {}", e.getMessage(), e);
+            
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", e.getMessage());
+            
+            return errorResult;
+        }
+    }
+
+    /**
+     * 韬唤璇佽瘑鍒�
+     * @param imagePath 鍥剧墖璺緞
+     * @param isFront true涓烘闈紝false涓哄弽闈�
+     * @return 璇嗗埆缁撴灉
+     */
+    public static JSONObject idCardRecognize(String imagePath, boolean isFront) {
+        try {
+            AipOcr client = getClient();
+            
+            HashMap<String, String> options = new HashMap<String, String>();
+            String idCardSide = isFront ? "front" : "back";
+            
+            org.json.JSONObject res = client.idcard(imagePath, idCardSide, options);
+            log.info("鐧惧害OCR韬唤璇佽瘑鍒垚鍔燂紝鍥剧墖璺緞: {}锛屾柟鍚�: {}", imagePath, idCardSide);
+            
+            JSONObject result = new JSONObject();
+            result.put("success", true);
+            result.put("data", JSON.parseObject(res.toString()));
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("鐧惧害OCR韬唤璇佽瘑鍒け璐�: {}", e.getMessage(), e);
+            
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", e.getMessage());
+            
+            return errorResult;
+        }
+    }
+
+    /**
+     * 閾惰鍗¤瘑鍒�
+     * @param imagePath 鍥剧墖璺緞
+     * @return 璇嗗埆缁撴灉
+     */
+    public static JSONObject bankCardRecognize(String imagePath) {
+        try {
+            AipOcr client = getClient();
+            
+            org.json.JSONObject res = client.bankcard(imagePath, new HashMap<String, String>());
+            log.info("鐧惧害OCR閾惰鍗¤瘑鍒垚鍔燂紝鍥剧墖璺緞: {}", imagePath);
+            
+            JSONObject result = new JSONObject();
+            result.put("success", true);
+            result.put("data", JSON.parseObject(res.toString()));
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("鐧惧害OCR閾惰鍗¤瘑鍒け璐�: {}", e.getMessage(), e);
+            
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", e.getMessage());
+            
+            return errorResult;
+        }
+    }
+
+    /**
+     * 钀ヤ笟鎵х収璇嗗埆
+     * @param imagePath 鍥剧墖璺緞
+     * @return 璇嗗埆缁撴灉
+     */
+    public static JSONObject businessLicenseRecognize(String imagePath) {
+        try {
+            AipOcr client = getClient();
+            
+            HashMap<String, String> options = new HashMap<String, String>();
+            
+            org.json.JSONObject res = client.businessLicense(imagePath, options);
+            log.info("鐧惧害OCR钀ヤ笟鎵х収璇嗗埆鎴愬姛锛屽浘鐗囪矾寰�: {}", imagePath);
+            
+            JSONObject result = new JSONObject();
+            result.put("success", true);
+            result.put("data", JSON.parseObject(res.toString()));
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("鐧惧害OCR钀ヤ笟鎵х収璇嗗埆澶辫触: {}", e.getMessage(), e);
+            
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", e.getMessage());
+            
+            return errorResult;
+        }
+    }
+
+    /**
+     * 浠庣櫨搴CR缁撴灉涓彁鍙栨枃鏈唴瀹�
+     * @param result 鐧惧害OCR杩斿洖鐨勭粨鏋滐紙fastjson鏍煎紡锛�
+     * @return 鎻愬彇鐨勬枃鏈唴瀹�
+     */
+    private static String extractContentFromBaiduResult(JSONObject result) {
+        StringBuilder content = new StringBuilder();
+        
+        if (result.containsKey("words_result") && result.getJSONArray("words_result") != null) {
+            JSONArray wordsResult = result.getJSONArray("words_result");
+            for (int i = 0; i < wordsResult.size(); i++) {
+                JSONObject wordResult = wordsResult.getJSONObject(i);
+                if (wordResult.containsKey("words")) {
+                    content.append(wordResult.getString("words")).append("\n");
+                }
+            }
+        }
+        
+        return content.toString().trim();
+    }
+
+    /**
+     * 浠庤瘑鍒粨鏋滀腑鎻愬彇鐩爣瀛楁锛堥噾棰濄�佹棩鏈熴�佸娉ㄧ瓑锛�
+     * @param ocrResult OCR璇嗗埆鐨勫師濮嬬粨鏋�
+     * @return 鎻愬彇鍚庣殑鐩爣瀛楁
+     */
+    public static java.util.Map<String, String> extractTargetFields(JSONObject ocrResult) {
+        java.util.Map<String, String> extracted = new java.util.HashMap<>();
+
+        // 鏍¢獙OCR缁撴灉鏄惁鏈夋晥
+        if (!ocrResult.containsKey("success") || !ocrResult.getBooleanValue("success")) {
+            extracted.put("error", ocrResult.getString("error"));
+            return extracted;
+        }
+
+        // 鑾峰彇璇嗗埆鐨勬枃瀛楀唴瀹�
+        String content = ocrResult.getString("content");
+        if (content == null || content.isEmpty()) {
+            extracted.put("error", "OCR璇嗗埆缁撴灉涓虹┖");
+            return extracted;
+        }
+
+        // 鍦ㄥ唴瀹逛腑鏌ユ壘鐗瑰畾鍏抽敭璇�
+        String[] lines = content.split("\n");
+        for (String line : lines) {
+            line = line.trim();
+            
+            // 鏌ユ壘閲戦鐩稿叧淇℃伅
+            if (line.contains("閲戦") || line.contains("鍚堣") || line.contains("鎬昏") || line.matches(".*\\d+\\.\\d{2}.*")) {
+                if (!extracted.containsKey("totalAmount")) {
+                    extracted.put("totalAmount", line);
+                }
+            }
+            
+            // 鏌ユ壘鏃ユ湡鐩稿叧淇℃伅
+            if (line.contains("鏃ユ湡") || line.matches(".*\\d{4}[-/骞碷\\d{1,2}[-/鏈圿\\d{1,2}.*")) {
+                if (!extracted.containsKey("date")) {
+                    extracted.put("date", line);
+                }
+            }
+            
+            // 鏌ユ壘澶囨敞鐩稿叧淇℃伅
+            if (line.contains("澶囨敞") || line.contains("璇存槑")) {
+                if (!extracted.containsKey("remark")) {
+                    extracted.put("remark", line);
+                }
+            }
+        }
+
+        // 濡傛灉娌℃湁鎵惧埌鐗瑰畾瀛楁锛岃繑鍥炲叏鏂�
+        if (extracted.isEmpty()) {
+            extracted.put("fullText", content);
+        }
+
+        return extracted;
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..6597b02
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/TencentOCRUtil.java
@@ -0,0 +1,506 @@
+package com.ruoyi.system.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.tencentcloudapi.common.AbstractModel;
+import com.tencentcloudapi.common.Credential;
+import com.tencentcloudapi.common.profile.ClientProfile;
+import com.tencentcloudapi.common.profile.HttpProfile;
+import com.tencentcloudapi.common.exception.TencentCloudSDKException;
+import com.tencentcloudapi.ocr.v20181119.OcrClient;
+import com.tencentcloudapi.ocr.v20181119.models.*;
+
+import com.ruoyi.system.config.TencentOCRConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.util.Base64;
+import java.nio.file.Files;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 鑵捐浜慜CR宸ュ叿绫�
+ * 浣跨敤鑵捐浜慜CR鏈嶅姟杩涜鏂囧瓧璇嗗埆
+ * 鏀寔閫氱敤鏂囧瓧璇嗗埆銆佹墜鍐欎綋璇嗗埆绛夊绉嶈瘑鍒被鍨�
+ * 
+ * 浣跨敤绀轰緥锛�
+ * // 閫氱敤鏂囧瓧璇嗗埆
+ * JSONObject result = TencentOCRUtil.generalRecognize("path/to/image.jpg");
+ * 
+ * // 鎵嬪啓浣撹瘑鍒�
+ * JSONObject result = TencentOCRUtil.handwritingRecognize("path/to/image.jpg");
+ */
+@Component
+public class TencentOCRUtil {
+
+    private static final Logger log = LoggerFactory.getLogger(TencentOCRUtil.class);
+
+    private static TencentOCRConfig staticTencentOcrConfig;
+
+    @Autowired
+    public void setTencentOcrConfig(TencentOCRConfig tencentOcrConfig) {
+        TencentOCRUtil.staticTencentOcrConfig = tencentOcrConfig;
+    }
+
+    /**
+     * 鑾峰彇鑵捐浜慜CR瀹㈡埛绔疄渚�
+     * @return OcrClient瀹㈡埛绔疄渚�
+     * @throws TencentCloudSDKException SDK寮傚父
+     */
+    private static OcrClient getClient() throws TencentCloudSDKException {
+        Credential cred = new Credential(
+            staticTencentOcrConfig.getSecretId(), 
+            staticTencentOcrConfig.getSecretKey()
+        );
+
+        HttpProfile httpProfile = new HttpProfile();
+        httpProfile.setEndpoint(staticTencentOcrConfig.getEndpoint());
+
+        ClientProfile clientProfile = new ClientProfile();
+        clientProfile.setHttpProfile(httpProfile);
+
+        return new OcrClient(cred, "", clientProfile);
+    }
+
+    /**
+     * 閫氱敤鏂囧瓧璇嗗埆锛堝浘鐗囪矾寰勶級
+     * @param imagePath 鍥剧墖璺緞
+     * @return 璇嗗埆缁撴灉
+     */
+    public static JSONObject generalRecognize(String imagePath) {
+        try {
+            byte[] imageBytes = Files.readAllBytes(new File(imagePath).toPath());
+            String base64Image = Base64.getEncoder().encodeToString(imageBytes);
+            
+            OcrClient client = getClient();
+            GeneralBasicOCRRequest req = new GeneralBasicOCRRequest();
+            req.setImageBase64(base64Image);
+            
+            GeneralBasicOCRResponse resp = client.GeneralBasicOCR(req);
+            
+            log.info("鑵捐浜慜CR閫氱敤鏂囧瓧璇嗗埆鎴愬姛锛屽浘鐗囪矾寰�: {}", imagePath);
+            
+            JSONObject result = new JSONObject();
+            result.put("success", true);
+            result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp)));
+            result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp))));
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("鑵捐浜慜CR閫氱敤鏂囧瓧璇嗗埆澶辫触: {}", e.getMessage(), e);
+            
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", e.getMessage());
+            
+            return errorResult;
+        }
+    }
+
+    /**
+     * 閫氱敤鏂囧瓧璇嗗埆锛堟枃浠跺璞★級
+     * @param imageFile 鍥剧墖鏂囦欢瀵硅薄
+     * @return 璇嗗埆缁撴灉
+     */
+    public static JSONObject generalRecognize(File imageFile) {
+        try {
+            byte[] imageBytes = Files.readAllBytes(imageFile.toPath());
+            String base64Image = Base64.getEncoder().encodeToString(imageBytes);
+            
+            OcrClient client = getClient();
+            GeneralBasicOCRRequest req = new GeneralBasicOCRRequest();
+            req.setImageBase64(base64Image);
+            
+            GeneralBasicOCRResponse resp = client.GeneralBasicOCR(req);
+            
+            log.info("鑵捐浜慜CR閫氱敤鏂囧瓧璇嗗埆鎴愬姛锛屾枃浠跺悕: {}", imageFile.getName());
+            
+            JSONObject result = new JSONObject();
+            result.put("success", true);
+            result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp)));
+            result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp))));
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("鑵捐浜慜CR閫氱敤鏂囧瓧璇嗗埆澶辫触: {}", e.getMessage(), e);
+            
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", e.getMessage());
+            
+            return errorResult;
+        }
+    }
+
+    /**
+     * 閫氱敤鏂囧瓧璇嗗埆锛堝浘鐗囧瓧鑺傛暟缁勶級
+     * @param imageBytes 鍥剧墖瀛楄妭鏁扮粍
+     * @return 璇嗗埆缁撴灉
+     */
+    public static JSONObject generalRecognize(byte[] imageBytes) {
+        try {
+            String base64Image = Base64.getEncoder().encodeToString(imageBytes);
+            
+            OcrClient client = getClient();
+            GeneralBasicOCRRequest req = new GeneralBasicOCRRequest();
+            req.setImageBase64(base64Image);
+            
+            GeneralBasicOCRResponse resp = client.GeneralBasicOCR(req);
+            
+            log.info("鑵捐浜慜CR閫氱敤鏂囧瓧璇嗗埆鎴愬姛锛屽瓧鑺傛暟缁勯暱搴�: {}", imageBytes.length);
+            
+            JSONObject result = new JSONObject();
+            result.put("success", true);
+            result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp)));
+            result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp))));
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("鑵捐浜慜CR閫氱敤鏂囧瓧璇嗗埆澶辫触: {}", e.getMessage(), e);
+            
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", e.getMessage());
+            
+            return errorResult;
+        }
+    }
+
+    /**
+     * 楂樼簿搴︽枃瀛楄瘑鍒�
+     * @param imagePath 鍥剧墖璺緞
+     * @return 璇嗗埆缁撴灉
+     */
+    public static JSONObject accurateRecognize(String imagePath) {
+        try {
+            byte[] imageBytes = Files.readAllBytes(new File(imagePath).toPath());
+            String base64Image = Base64.getEncoder().encodeToString(imageBytes);
+            
+            OcrClient client = getClient();
+            GeneralAccurateOCRRequest req = new GeneralAccurateOCRRequest();
+            req.setImageBase64(base64Image);
+            
+            GeneralAccurateOCRResponse resp = client.GeneralAccurateOCR(req);
+            
+            log.info("鑵捐浜慜CR楂樼簿搴︽枃瀛楄瘑鍒垚鍔燂紝鍥剧墖璺緞: {}", imagePath);
+            
+            JSONObject result = new JSONObject();
+            result.put("success", true);
+            result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp)));
+            result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp))));
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("鑵捐浜慜CR楂樼簿搴︽枃瀛楄瘑鍒け璐�: {}", e.getMessage(), e);
+            
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", e.getMessage());
+            
+            return errorResult;
+        }
+    }
+
+    public static JSONObject handwritingRecognize(String imagePath, String[] itemNames) {
+         try {
+            byte[] imageBytes = Files.readAllBytes(new File(imagePath).toPath());
+            String base64Image = Base64.getEncoder().encodeToString(imageBytes);
+            
+            OcrClient client = getClient();
+
+            ExtractDocMultiRequest req = new ExtractDocMultiRequest();
+            req.setImageBase64(base64Image);
+            // {"鎮h�呯鍚嶏紙鎵嬪嵃锛�", "绛惧瓧浜鸿韩浠借瘉鍙风爜", "鏃ユ湡", "鑱旂郴鐢佃瘽", "鏈汉", "绛惧瓧浜轰笌鎮h�呭叧绯�"}
+            req.setItemNames(itemNames != null ? itemNames : new String[]{"鎮h�呭鍚�", "鎬у埆", "骞撮緞", "韬唤璇佸彿", "璇婃柇", "闇�鏀粯杞繍璐圭敤", "琛岀▼", "寮�濮嬫椂闂�", "缁撴潫鏃堕棿", "瀹跺睘绛惧悕"});
+            req.setOutputLanguage("cn");
+            req.setReturnFullText(false);
+            req.setItemNamesShowMode(false);
+            ExtractDocMultiResponse resp = client.ExtractDocMulti(req);
+
+            log.info("鑵捐浜慜CR鎵嬪啓浣撹瘑鍒垚鍔燂紝鍥剧墖璺緞: {}", imagePath);
+            
+            // 瑙f瀽鍝嶅簲鏁版嵁
+            JSONObject responseData = JSON.parseObject(AbstractModel.toJsonString(resp));
+            
+            
+            log.info("鎵嬪啓浣撹瘑鍒彁鍙栧埌 {} 涓瓧娈�", responseData.size());
+            return responseData;
+            
+        } catch (Exception e) {
+            log.error("鑵捐浜慜CR鎵嬪啓浣撹瘑鍒け璐�: {}", e.getMessage(), e);
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("error", e.getMessage());
+            return errorResult;
+        }
+    }
+    /**
+     * 鎵嬪啓浣撹瘑鍒�
+     * @param imagePath 鍥剧墖璺緞
+     * @param itemNames 闇�瑕佹彁鍙栫殑瀛楁鍚嶇О鏁扮粍
+     * @return 璇嗗埆缁撴灉 Map锛宬ey涓篈utoName鐨勫�硷紝value涓篈utoContent鐨勫��
+     */
+    public static Map<String, String> handwritingRecognizeWith(String imagePath, String[] itemNames) {
+        Map<String, String> resultMap = new HashMap<>();
+        try {
+           JSONObject responseData = handwritingRecognize(imagePath, itemNames);
+            
+            // 浠嶴tructuralList涓彁鍙栨暟鎹�
+            if (responseData.containsKey("StructuralList") && responseData.getJSONArray("StructuralList") != null) {
+                JSONArray structuralList = responseData.getJSONArray("StructuralList");
+                for (int i = 0; i < structuralList.size(); i++) {
+                    JSONObject structural = structuralList.getJSONObject(i);
+                    if (structural.containsKey("Groups") && structural.getJSONArray("Groups") != null) {
+                        JSONArray groups = structural.getJSONArray("Groups");
+                        for (int j = 0; j < groups.size(); j++) {
+                            JSONObject group = groups.getJSONObject(j);
+                            if (group.containsKey("Lines") && group.getJSONArray("Lines") != null) {
+                                JSONArray lines = group.getJSONArray("Lines");
+                                for (int k = 0; k < lines.size(); k++) {
+                                    JSONObject line = lines.getJSONObject(k);
+                                    String autoName = null;
+                                    String autoContent = null;
+                                    
+                                    // 鎻愬彇AutoName
+                                    if (line.containsKey("Key") && line.getJSONObject("Key") != null) {
+                                        JSONObject key = line.getJSONObject("Key");
+                                        if (key.containsKey("AutoName")) {
+                                            autoName = key.getString("AutoName");
+                                        }
+                                    }
+                                    
+                                    // 鎻愬彇AutoContent
+                                    if (line.containsKey("Value") && line.getJSONObject("Value") != null) {
+                                        JSONObject value = line.getJSONObject("Value");
+                                        if (value.containsKey("AutoContent")) {
+                                            autoContent = value.getString("AutoContent");
+                                        }
+                                    }
+                                    
+                                    // 灏嗛敭鍊煎鏀惧叆缁撴灉Map
+                                    if (autoName != null && autoContent != null) {
+                                        resultMap.put(autoName, autoContent);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            
+            log.info("鎵嬪啓浣撹瘑鍒彁鍙栧埌 {} 涓瓧娈�", resultMap.size());
+            return resultMap;
+            
+        } catch (Exception e) {
+            log.error("鑵捐浜慜CR鎵嬪啓浣撹瘑鍒け璐�: {}", e.getMessage(), e);
+            resultMap.put("error", e.getMessage());
+            return resultMap;
+        }
+    }
+
+    /**
+     * 韬唤璇佽瘑鍒�
+     * @param imagePath 鍥剧墖璺緞
+     * @param cardSide 韬唤璇佹鍙嶉潰锛�"FRONT"琛ㄧず姝i潰锛�"BACK"琛ㄧず鍙嶉潰
+     * @return 璇嗗埆缁撴灉
+     */
+    public static JSONObject idCardRecognize(String imagePath, String cardSide) {
+        try {
+            byte[] imageBytes = Files.readAllBytes(new File(imagePath).toPath());
+            String base64Image = Base64.getEncoder().encodeToString(imageBytes);
+            
+            OcrClient client = getClient();
+            IDCardOCRRequest req = new IDCardOCRRequest();
+            req.setImageBase64(base64Image);
+            req.setCardSide(cardSide);
+            
+            IDCardOCRResponse resp = client.IDCardOCR(req);
+            
+            log.info("鑵捐浜慜CR韬唤璇佽瘑鍒垚鍔燂紝鍥剧墖璺緞: {}锛屾柟鍚�: {}", imagePath, cardSide);
+            
+            JSONObject result = new JSONObject();
+            result.put("success", true);
+            result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp)));
+            result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp))));
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("鑵捐浜慜CR韬唤璇佽瘑鍒け璐�: {}", e.getMessage(), e);
+            
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", e.getMessage());
+            
+            return errorResult;
+        }
+    }
+
+    /**
+     * 閾惰鍗¤瘑鍒�
+     * @param imagePath 鍥剧墖璺緞
+     * @return 璇嗗埆缁撴灉
+     */
+    public static JSONObject bankCardRecognize(String imagePath) {
+        try {
+            byte[] imageBytes = Files.readAllBytes(new File(imagePath).toPath());
+            String base64Image = Base64.getEncoder().encodeToString(imageBytes);
+            
+            OcrClient client = getClient();
+            BankCardOCRRequest req = new BankCardOCRRequest();
+            req.setImageBase64(base64Image);
+            
+            BankCardOCRResponse resp = client.BankCardOCR(req);
+            
+            log.info("鑵捐浜慜CR閾惰鍗¤瘑鍒垚鍔燂紝鍥剧墖璺緞: {}", imagePath);
+            
+            JSONObject result = new JSONObject();
+            result.put("success", true);
+            result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp)));
+            result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp))));
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("鑵捐浜慜CR閾惰鍗¤瘑鍒け璐�: {}", e.getMessage(), e);
+            
+            JSONObject errorResult = new JSONObject();
+            errorResult.put("success", false);
+            errorResult.put("error", e.getMessage());
+            
+            return errorResult;
+        }
+    }
+
+    /**
+     * 浠庤吘璁簯OCR缁撴灉涓彁鍙栫函鏂囨湰鍐呭
+     * @param result OCR璇嗗埆缁撴灉
+     * @return 鎻愬彇鐨勬枃鏈唴瀹�
+     */
+    private static String extractContentFromTencentResult(JSONObject result) {
+        StringBuilder content = new StringBuilder();
+        
+        // 澶勭悊閫氱敤OCR缁撴灉
+        if (result.containsKey("TextDetections") && result.getJSONArray("TextDetections") != null) {
+            JSONArray textDetections = result.getJSONArray("TextDetections");
+            for (int i = 0; i < textDetections.size(); i++) {
+                JSONObject detection = textDetections.getJSONObject(i);
+                if (detection.containsKey("DetectedText")) {
+                    content.append(detection.getString("DetectedText")).append("\n");
+                }
+            }
+        }
+        // 澶勭悊鎵嬪啓浣揙CR缁撴灉
+        else if (result.containsKey("Items") && result.getJSONArray("Items") != null) {
+            JSONArray items = result.getJSONArray("Items");
+            for (int i = 0; i < items.size(); i++) {
+                JSONObject item = items.getJSONObject(i);
+                if (item.containsKey("Itemstring")) {
+                    content.append(item.getString("Itemstring")).append("\n");
+                }
+            }
+        }
+        // 澶勭悊韬唤璇丱CR缁撴灉
+        else if (result.containsKey("Name") || result.containsKey("Sex") || result.containsKey("Nation") ||
+                 result.containsKey("Birth") || result.containsKey("Address") || result.containsKey("IdNum")) {
+            if (result.containsKey("Name") && result.getString("Name") != null) {
+                content.append("濮撳悕: ").append(result.getString("Name")).append("\n");
+            }
+            if (result.containsKey("Sex") && result.getString("Sex") != null) {
+                content.append("鎬у埆: ").append(result.getString("Sex")).append("\n");
+            }
+            if (result.containsKey("Nation") && result.getString("Nation") != null) {
+                content.append("姘戞棌: ").append(result.getString("Nation")).append("\n");
+            }
+            if (result.containsKey("Birth") && result.getString("Birth") != null) {
+                content.append("鍑虹敓: ").append(result.getString("Birth")).append("\n");
+            }
+            if (result.containsKey("Address") && result.getString("Address") != null) {
+                content.append("鍦板潃: ").append(result.getString("Address")).append("\n");
+            }
+            if (result.containsKey("IdNum") && result.getString("IdNum") != null) {
+                content.append("韬唤璇佸彿: ").append(result.getString("IdNum")).append("\n");
+            }
+        }
+        // 澶勭悊閾惰鍗CR缁撴灉
+        else if (result.containsKey("CardNo") && result.getString("CardNo") != null) {
+            content.append("閾惰鍗″彿: ").append(result.getString("CardNo")).append("\n");
+        }
+        
+        return content.toString().trim();
+    }
+
+    /**
+     * 浠庤瘑鍒粨鏋滀腑鎻愬彇鐩爣瀛楁锛堥噾棰濄�佹棩鏈熴�佸娉ㄧ瓑锛�
+     * @param ocrResult OCR璇嗗埆鐨勫師濮嬬粨鏋�
+     * @return 鎻愬彇鍚庣殑鐩爣瀛楁
+     */
+    public static Map<String, String> extractTargetFields(JSONObject ocrResult) {
+        Map<String, String> extracted = new HashMap<>();
+
+        // 鏍¢獙OCR缁撴灉鏄惁鏈夋晥
+        if (!ocrResult.containsKey("success") || !ocrResult.getBooleanValue("success")) {
+            extracted.put("error", ocrResult.getString("error"));
+            return extracted;
+        }
+
+        // 鑾峰彇璇嗗埆鐨勬枃瀛楀唴瀹�
+        String content = ocrResult.getString("content");
+        if (content == null || content.isEmpty()) {
+            extracted.put("error", "OCR璇嗗埆缁撴灉涓虹┖");
+            return extracted;
+        }
+
+        // 鍦ㄥ唴瀹逛腑鏌ユ壘鐗瑰畾鍏抽敭璇�
+        String[] lines = content.split("\n");
+        for (String line : lines) {
+            line = line.trim();
+            
+            // 鏌ユ壘閲戦鐩稿叧淇℃伅
+            if (line.contains("閲戦") || line.contains("鍚堣") || line.contains("鎬昏") || line.matches(".*\\d+\\.\\d{2}.*")) {
+                if (!extracted.containsKey("totalAmount")) {
+                    extracted.put("totalAmount", line);
+                }
+            }
+            
+            // 鏌ユ壘鏃ユ湡鐩稿叧淇℃伅
+            if (line.contains("鏃ユ湡") || line.matches(".*\\d{4}[-/骞碷\\d{1,2}[-/鏈圿\\d{1,2}.*")) {
+                if (!extracted.containsKey("date")) {
+                    extracted.put("date", line);
+                }
+            }
+            
+            // 鏌ユ壘澶囨敞鐩稿叧淇℃伅
+            if (line.contains("澶囨敞") || line.contains("璇存槑")) {
+                if (!extracted.containsKey("remark")) {
+                    extracted.put("remark", line);
+                }
+            }
+        }
+
+        // 濡傛灉娌℃湁鎵惧埌鐗瑰畾瀛楁锛岃繑鍥炲叏鏂�
+        if (extracted.isEmpty()) {
+            extracted.put("fullText", content);
+        }
+
+        return extracted;
+    }
+
+    /**
+     * 鎵嬪啓浣撹瘑鍒紙浣跨敤榛樿瀛楁锛�
+     * @param imagePath 鍥剧墖璺緞
+     * @return 璇嗗埆缁撴灉 Map锛宬ey涓篈utoName鐨勫�硷紝value涓篈utoContent鐨勫��
+     */
+    public static Map<String, String> handwritingRecognize(String imagePath) {
+        String[] defaultItemNames = {"鎮h�呭鍚�", "鎬у埆", "骞撮緞", "韬唤璇佸彿", "璇婃柇", "闇�鏀粯杞繍璐圭敤", "琛岀▼", "寮�濮嬫椂闂�", "缁撴潫鏃堕棿", "瀹跺睘绛惧悕"};
+        return handwritingRecognizeWith(imagePath, defaultItemNames);
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/resources/mapper/system/DispatchOrdMapper.xml b/ruoyi-system/src/main/resources/mapper/system/DispatchOrdMapper.xml
index 565843e..3abe0f2 100644
--- a/ruoyi-system/src/main/resources/mapper/system/DispatchOrdMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/DispatchOrdMapper.xml
@@ -115,5 +115,12 @@
             DispatchOrdCancelReasonTXT = #{cancelReasonText}
         where DispatchOrdID = #{dispatchOrdID}
     </update>
+    
+    <!-- 鏇存柊璋冨害鍗曞疄闄呭紑濮嬫椂闂� -->
+    <update id="updateDispatchOrdActualDate">
+        update DispatchOrd 
+        set DispatchOrdActualDate = #{actualDate}
+        where DispatchOrdID = #{dispatchOrdID}
+    </update>
 
 </mapper> 
\ No newline at end of file
diff --git a/ruoyi-system/src/main/resources/mapper/system/TbHospDataMapper.xml b/ruoyi-system/src/main/resources/mapper/system/TbHospDataMapper.xml
index 050df08..f5ceab2 100644
--- a/ruoyi-system/src/main/resources/mapper/system/TbHospDataMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/TbHospDataMapper.xml
@@ -21,6 +21,7 @@
         <result property="hospIntroducerId"    column="hosp_introducer_id"   />
         <result property="hospIntroducerDate"  column="hosp_introducer_date" />
         <result property="hospLevel"           column="hosp_level"           />
+        <result property="hospKeywords"        column="hosp_keywords"        />
         <result property="status"              column="status"               />
         <result property="remark"              column="remark"               />
         <result property="createBy"            column="create_by"            />
@@ -33,7 +34,7 @@
         select hosp_id, legacy_hosp_id, hosp_name, hosp_city_id, hosp_short, 
                hops_province, hops_city, hops_area, hosp_address, hosp_tel, 
                hosp_unit_id, hosp_state, hosp_oa_id, hosp_introducer_id, 
-               hosp_introducer_date, hosp_level, status, remark, 
+               hosp_introducer_date, hosp_level, hosp_keywords, status, remark, 
                create_by, create_time, update_by, update_time
         from tb_hosp_data
     </sql>
@@ -90,6 +91,7 @@
             <if test="hospIntroducerId != null">hosp_introducer_id,</if>
             <if test="hospIntroducerDate != null">hosp_introducer_date,</if>
             <if test="hospLevel != null">hosp_level,</if>
+            <if test="hospKeywords != null">hosp_keywords,</if>
             <if test="status != null">status,</if>
             <if test="remark != null">remark,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
@@ -110,6 +112,7 @@
             <if test="hospIntroducerId != null">#{hospIntroducerId},</if>
             <if test="hospIntroducerDate != null">#{hospIntroducerDate},</if>
             <if test="hospLevel != null">#{hospLevel},</if>
+            <if test="hospKeywords != null">#{hospKeywords},</if>
             <if test="status != null">#{status},</if>
             <if test="remark != null">#{remark},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
@@ -135,6 +138,7 @@
             <if test="hospIntroducerId != null">hosp_introducer_id = #{hospIntroducerId},</if>
             <if test="hospIntroducerDate != null">hosp_introducer_date = #{hospIntroducerDate},</if>
             <if test="hospLevel != null">hosp_level = #{hospLevel},</if>
+            <if test="hospKeywords != null">hosp_keywords = #{hospKeywords},</if>
             <if test="status != null">status = #{status},</if>
             <if test="remark != null">remark = #{remark},</if>
             <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
@@ -153,5 +157,23 @@
             #{hospId}
         </foreach>
     </delete>
+    
+    <!-- 鏍规嵁鍒嗚瘝鍏抽敭璇嶉杩囨护鍖婚櫌鏁版嵁 -->
+    <select id="selectTbHospDataByKeywords" resultMap="TbHospDataResult">
+        <include refid="selectTbHospDataVo"/>
+        <where>
+            <if test="status != null and status != ''">
+                and status = #{status}
+            </if>
+            <if test="keywords != null and keywords.size() > 0">
+                and (
+                    <foreach collection="keywords" item="keyword" separator="OR">
+                        hosp_keywords like concat('%', #{keyword}, '%')
+                    </foreach>
+                )
+            </if>
+        </where>
+        order by hosp_id desc
+    </select>
 
 </mapper>
diff --git a/ruoyi-system/src/main/resources/mapper/system/VehicleAbnormalAlertMapper.xml b/ruoyi-system/src/main/resources/mapper/system/VehicleAbnormalAlertMapper.xml
new file mode 100644
index 0000000..7e98d1d
--- /dev/null
+++ b/ruoyi-system/src/main/resources/mapper/system/VehicleAbnormalAlertMapper.xml
@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.VehicleAbnormalAlertMapper">
+    
+    <resultMap type="VehicleAbnormalAlert" id="VehicleAbnormalAlertResult">
+        <result property="alertId"    column="alert_id"    />
+        <result property="vehicleId"    column="vehicle_id"    />
+        <result property="vehicleNo"    column="vehicle_no"    />
+        <result property="alertDate"    column="alert_date"    />
+        <result property="alertTime"    column="alert_time"    />
+        <result property="mileage"    column="mileage"    />
+        <result property="alertType"    column="alert_type"    />
+        <result property="alertReason"    column="alert_reason"    />
+        <result property="startTime"    column="start_time"    />
+        <result property="endTime"    column="end_time"    />
+        <result property="alertCount"    column="alert_count"    />
+        <result property="status"    column="status"    />
+        <result property="handlerId"    column="handler_id"    />
+        <result property="handlerName"    column="handler_name"    />
+        <result property="handleTime"    column="handle_time"    />
+        <result property="handleRemark"    column="handle_remark"    />
+        <result property="notifyStatus"    column="notify_status"    />
+        <result property="notifyTime"    column="notify_time"    />
+        <result property="notifyUsers"    column="notify_users"    />
+        <result property="deptId"    column="dept_id"    />
+        <result property="deptName"    column="dept_name"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateTime"    column="update_time"    />
+    </resultMap>
+
+    <sql id="selectVehicleAbnormalAlertVo">
+        select alert_id, vehicle_id, vehicle_no, alert_date, alert_time, mileage, alert_type, alert_reason, 
+               start_time, end_time, alert_count, status, handler_id, handler_name, handle_time, handle_remark, 
+               notify_status, notify_time, notify_users, dept_id, dept_name, create_time, update_time
+        from tb_vehicle_abnormal_alert
+    </sql>
+
+    <select id="selectVehicleAbnormalAlertList" parameterType="VehicleAbnormalAlert" resultMap="VehicleAbnormalAlertResult">
+        <include refid="selectVehicleAbnormalAlertVo"/>
+        <where>  
+            <if test="vehicleId != null "> and vehicle_id = #{vehicleId}</if>
+            <if test="vehicleNo != null  and vehicleNo != ''"> and vehicle_no like concat('%', #{vehicleNo}, '%')</if>
+            <if test="alertDate != null "> and date(alert_date) = date(#{alertDate})</if>
+            <if test="params.beginAlertTime != null and params.beginAlertTime != ''"><!-- 寮�濮嬫椂闂存绱� -->
+                AND date_format(alert_time,'%y%m%d') &gt;= date_format(#{params.beginAlertTime},'%y%m%d')
+            </if>
+            <if test="params.endAlertTime != null and params.endAlertTime != ''"><!-- 缁撴潫鏃堕棿妫�绱� -->
+                AND date_format(alert_time,'%y%m%d') &lt;= date_format(#{params.endAlertTime},'%y%m%d')
+            </if>
+            <if test="alertType != null  and alertType != ''"> and alert_type = #{alertType}</if>
+            <if test="status != null  and status != ''"> and status = #{status}</if>
+            <if test="deptId != null "> and dept_id = #{deptId}</if>
+            <if test="deptName != null  and deptName != ''"> and dept_name like concat('%', #{deptName}, '%')</if>
+        </where>
+        order by alert_time desc
+    </select>
+    
+    <select id="selectVehicleAbnormalAlertByAlertId" parameterType="Long" resultMap="VehicleAbnormalAlertResult">
+        <include refid="selectVehicleAbnormalAlertVo"/>
+        where alert_id = #{alertId}
+    </select>
+        
+    <insert id="insertVehicleAbnormalAlert" parameterType="VehicleAbnormalAlert" useGeneratedKeys="true" keyProperty="alertId">
+        insert into tb_vehicle_abnormal_alert
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="vehicleId != null">vehicle_id,</if>
+            <if test="vehicleNo != null and vehicleNo != ''">vehicle_no,</if>
+            <if test="alertDate != null">alert_date,</if>
+            <if test="alertTime != null">alert_time,</if>
+            <if test="mileage != null">mileage,</if>
+            <if test="alertType != null">alert_type,</if>
+            <if test="alertReason != null">alert_reason,</if>
+            <if test="startTime != null">start_time,</if>
+            <if test="endTime != null">end_time,</if>
+            <if test="alertCount != null">alert_count,</if>
+            <if test="status != null">status,</if>
+            <if test="handlerId != null">handler_id,</if>
+            <if test="handlerName != null">handler_name,</if>
+            <if test="handleTime != null">handle_time,</if>
+            <if test="handleRemark != null">handle_remark,</if>
+            <if test="notifyStatus != null">notify_status,</if>
+            <if test="notifyTime != null">notify_time,</if>
+            <if test="notifyUsers != null">notify_users,</if>
+            <if test="deptId != null">dept_id,</if>
+            <if test="deptName != null">dept_name,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateTime != null">update_time,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="vehicleId != null">#{vehicleId},</if>
+            <if test="vehicleNo != null and vehicleNo != ''">#{vehicleNo},</if>
+            <if test="alertDate != null">#{alertDate},</if>
+            <if test="alertTime != null">#{alertTime},</if>
+            <if test="mileage != null">#{mileage},</if>
+            <if test="alertType != null">#{alertType},</if>
+            <if test="alertReason != null">#{alertReason},</if>
+            <if test="startTime != null">#{startTime},</if>
+            <if test="endTime != null">#{endTime},</if>
+            <if test="alertCount != null">#{alertCount},</if>
+            <if test="status != null">#{status},</if>
+            <if test="handlerId != null">#{handlerId},</if>
+            <if test="handlerName != null">#{handlerName},</if>
+            <if test="handleTime != null">#{handleTime},</if>
+            <if test="handleRemark != null">#{handleRemark},</if>
+            <if test="notifyStatus != null">#{notifyStatus},</if>
+            <if test="notifyTime != null">#{notifyTime},</if>
+            <if test="notifyUsers != null">#{notifyUsers},</if>
+            <if test="deptId != null">#{deptId},</if>
+            <if test="deptName != null">#{deptName},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+         </trim>
+    </insert>
+
+    <update id="updateVehicleAbnormalAlert" parameterType="VehicleAbnormalAlert">
+        update tb_vehicle_abnormal_alert
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="vehicleId != null">vehicle_id = #{vehicleId},</if>
+            <if test="vehicleNo != null and vehicleNo != ''">vehicle_no = #{vehicleNo},</if>
+            <if test="alertDate != null">alert_date = #{alertDate},</if>
+            <if test="alertTime != null">alert_time = #{alertTime},</if>
+            <if test="mileage != null">mileage = #{mileage},</if>
+            <if test="alertType != null">alert_type = #{alertType},</if>
+            <if test="alertReason != null">alert_reason = #{alertReason},</if>
+            <if test="startTime != null">start_time = #{startTime},</if>
+            <if test="endTime != null">end_time = #{endTime},</if>
+            <if test="alertCount != null">alert_count = #{alertCount},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="handlerId != null">handler_id = #{handlerId},</if>
+            <if test="handlerName != null">handler_name = #{handlerName},</if>
+            <if test="handleTime != null">handle_time = #{handleTime},</if>
+            <if test="handleRemark != null">handle_remark = #{handleRemark},</if>
+            <if test="notifyStatus != null">notify_status = #{notifyStatus},</if>
+            <if test="notifyTime != null">notify_time = #{notifyTime},</if>
+            <if test="notifyUsers != null">notify_users = #{notifyUsers},</if>
+            <if test="deptId != null">dept_id = #{deptId},</if>
+            <if test="deptName != null">dept_name = #{deptName},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+        </trim>
+        where alert_id = #{alertId}
+    </update>
+
+    <delete id="deleteVehicleAbnormalAlertByAlertId" parameterType="Long">
+        delete from tb_vehicle_abnormal_alert where alert_id = #{alertId}
+    </delete>
+
+    <delete id="deleteVehicleAbnormalAlertByAlertIds" parameterType="String">
+        delete from tb_vehicle_abnormal_alert where alert_id in 
+        <foreach item="alertId" collection="array" open="(" separator="," close=")">
+            #{alertId}
+        </foreach>
+    </delete>
+    
+    <select id="selectDailyAlertCount" resultType="int">
+        select count(*) from tb_vehicle_abnormal_alert
+        where vehicle_id = #{vehicleId} and date(alert_date) = date(#{alertDate})
+    </select>
+    
+    <select id="selectLastAlertTime" resultType="java.util.Date">
+        select max(alert_time) from tb_vehicle_abnormal_alert
+        where vehicle_id = #{vehicleId}
+    </select>
+    
+    <update id="batchHandleAlert">
+        update tb_vehicle_abnormal_alert
+        set status = '1',
+            handler_id = #{handlerId},
+            handler_name = #{handlerName},
+            handle_time = now(),
+            handle_remark = #{handleRemark}
+        where alert_id in
+        <foreach item="alertId" collection="alertIds" open="(" separator="," close=")">
+            #{alertId}
+        </foreach>
+    </update>
+</mapper>
diff --git a/ruoyi-system/src/main/resources/mapper/system/VehicleAlertConfigMapper.xml b/ruoyi-system/src/main/resources/mapper/system/VehicleAlertConfigMapper.xml
new file mode 100644
index 0000000..27daa3f
--- /dev/null
+++ b/ruoyi-system/src/main/resources/mapper/system/VehicleAlertConfigMapper.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.VehicleAlertConfigMapper">
+    
+    <resultMap type="VehicleAlertConfig" id="VehicleAlertConfigResult">
+        <result property="configId"    column="config_id"    />
+        <result property="configType"    column="config_type"    />
+        <result property="deptId"    column="dept_id"    />
+        <result property="vehicleId"    column="vehicle_id"    />
+        <result property="mileageThreshold"    column="mileage_threshold"    />
+        <result property="dailyAlertLimit"    column="daily_alert_limit"    />
+        <result property="alertInterval"    column="alert_interval"    />
+        <result property="notifyUserIds"    column="notify_user_ids"    />
+        <result property="status"    column="status"    />
+        <result property="remark"    column="remark"    />
+        <result property="createBy"    column="create_by"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateBy"    column="update_by"    />
+        <result property="updateTime"    column="update_time"    />
+    </resultMap>
+
+    <sql id="selectVehicleAlertConfigVo">
+        select c.config_id, c.config_type, c.dept_id, c.vehicle_id, c.mileage_threshold, 
+               c.daily_alert_limit, c.alert_interval, c.notify_user_ids, c.status, c.remark, 
+               c.create_by, c.create_time, c.update_by, c.update_time,
+               CASE 
+                   WHEN c.config_type = 'DEPT' THEN d.dept_name
+                   WHEN c.config_type = 'VEHICLE' THEN v.vehicle_no
+                   ELSE NULL
+               END as target_name
+        from tb_vehicle_alert_config c
+        left join sys_dept d on c.dept_id = d.dept_id
+        left join tb_vehicle_info v on c.vehicle_id = v.vehicle_id
+    </sql>
+
+    <select id="selectVehicleAlertConfigList" parameterType="VehicleAlertConfig" resultMap="VehicleAlertConfigResult">
+        <include refid="selectVehicleAlertConfigVo"/>
+        <where>  
+            <if test="configType != null  and configType != ''"> and c.config_type = #{configType}</if>
+            <if test="deptId != null "> and c.dept_id = #{deptId}</if>
+            <if test="vehicleId != null "> and c.vehicle_id = #{vehicleId}</if>
+            <if test="status != null  and status != ''"> and c.status = #{status}</if>
+        </where>
+        order by 
+            CASE c.config_type 
+                WHEN 'GLOBAL' THEN 3
+                WHEN 'DEPT' THEN 2
+                WHEN 'VEHICLE' THEN 1
+            END,
+            c.create_time desc
+    </select>
+    
+    <select id="selectVehicleAlertConfigByConfigId" parameterType="Long" resultMap="VehicleAlertConfigResult">
+        <include refid="selectVehicleAlertConfigVo"/>
+        where c.config_id = #{configId}
+    </select>
+
+    <select id="selectConfigByVehicle" resultMap="VehicleAlertConfigResult">
+        <include refid="selectVehicleAlertConfigVo"/>
+        where c.status = '0'
+        and (
+            (c.config_type = 'VEHICLE' and c.vehicle_id = #{vehicleId})
+            or (c.config_type = 'DEPT' and c.dept_id = #{deptId})
+            or c.config_type = 'GLOBAL'
+        )
+        order by 
+            CASE c.config_type 
+                WHEN 'VEHICLE' THEN 1
+                WHEN 'DEPT' THEN 2
+                WHEN 'GLOBAL' THEN 3
+            END
+        limit 1
+    </select>
+        
+    <insert id="insertVehicleAlertConfig" parameterType="VehicleAlertConfig" useGeneratedKeys="true" keyProperty="configId">
+        insert into tb_vehicle_alert_config
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="configType != null and configType != ''">config_type,</if>
+            <if test="deptId != null">dept_id,</if>
+            <if test="vehicleId != null">vehicle_id,</if>
+            <if test="mileageThreshold != null">mileage_threshold,</if>
+            <if test="dailyAlertLimit != null">daily_alert_limit,</if>
+            <if test="alertInterval != null">alert_interval,</if>
+            <if test="notifyUserIds != null">notify_user_ids,</if>
+            <if test="status != null">status,</if>
+            <if test="remark != null">remark,</if>
+            <if test="createBy != null">create_by,</if>
+            create_time
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="configType != null and configType != ''">#{configType},</if>
+            <if test="deptId != null">#{deptId},</if>
+            <if test="vehicleId != null">#{vehicleId},</if>
+            <if test="mileageThreshold != null">#{mileageThreshold},</if>
+            <if test="dailyAlertLimit != null">#{dailyAlertLimit},</if>
+            <if test="alertInterval != null">#{alertInterval},</if>
+            <if test="notifyUserIds != null">#{notifyUserIds},</if>
+            <if test="status != null">#{status},</if>
+            <if test="remark != null">#{remark},</if>
+            <if test="createBy != null">#{createBy},</if>
+            now()
+         </trim>
+    </insert>
+
+    <update id="updateVehicleAlertConfig" parameterType="VehicleAlertConfig">
+        update tb_vehicle_alert_config
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="configType != null and configType != ''">config_type = #{configType},</if>
+            <if test="deptId != null">dept_id = #{deptId},</if>
+            <if test="vehicleId != null">vehicle_id = #{vehicleId},</if>
+            <if test="mileageThreshold != null">mileage_threshold = #{mileageThreshold},</if>
+            <if test="dailyAlertLimit != null">daily_alert_limit = #{dailyAlertLimit},</if>
+            <if test="alertInterval != null">alert_interval = #{alertInterval},</if>
+            <if test="notifyUserIds != null">notify_user_ids = #{notifyUserIds},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            update_time = now()
+        </trim>
+        where config_id = #{configId}
+    </update>
+
+    <delete id="deleteVehicleAlertConfigByConfigId" parameterType="Long">
+        delete from tb_vehicle_alert_config where config_id = #{configId}
+    </delete>
+
+    <delete id="deleteVehicleAlertConfigByConfigIds" parameterType="String">
+        delete from tb_vehicle_alert_config where config_id in 
+        <foreach item="configId" collection="array" open="(" separator="," close=")">
+            #{configId}
+        </foreach>
+    </delete>
+</mapper>
diff --git a/ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml b/ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml
index 6282f5d..73671e3 100644
--- a/ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml
@@ -72,6 +72,19 @@
           AND segment_distance &gt;0
     </select>
     
+    <!-- 鏌ヨ杞﹁締鍦ㄦ寚瀹氭椂闂磋寖鍥村唴鐨勫垎娈甸噷绋� -->
+    <select id="selectSegmentsByTimeRange" resultMap="VehicleGpsSegmentMileageResult">
+        SELECT segment_id, vehicle_id, vehicle_no, segment_start_time, segment_end_time,
+               start_longitude, start_latitude, end_longitude, end_latitude,
+               segment_distance, gps_point_count, gps_ids, task_id, task_code, calculate_method
+        FROM tb_vehicle_gps_segment_mileage
+        WHERE vehicle_id = #{vehicleId}
+          AND segment_start_time &lt;= #{endTime}
+          AND segment_end_time &gt;= #{startTime}
+          AND segment_distance &gt; 0
+        ORDER BY segment_start_time
+    </select>
+    
     <!-- 鎸変换鍔D鏌ヨ鍒嗘閲岀▼鍒楄〃 -->
     <select id="selectSegmentsByTaskId" resultMap="VehicleGpsSegmentMileageResult">
         <include refid="selectVehicleGpsSegmentMileageVo"/>
diff --git a/ruoyi-ui/src/api/system/networkDiag.js b/ruoyi-ui/src/api/system/networkDiag.js
new file mode 100644
index 0000000..f89b4b7
--- /dev/null
+++ b/ruoyi-ui/src/api/system/networkDiag.js
@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+
+/**
+ * OCR鏈嶅姟杩炴帴璇婃柇
+ */
+export function diagOcrConnection() {
+  return request({
+    url: '/system/diag/ocrConnection',
+    method: 'get'
+  })
+}
+
+/**
+ * 閫氱敤缃戠粶杩為�氭�ф祴璇�
+ * @param {String} host - 鐩爣涓绘満
+ * @param {Number} port - 鐩爣绔彛
+ */
+export function testConnectivity(host, port) {
+  return request({
+    url: '/system/diag/testConnectivity',
+    method: 'post',
+    data: {
+      host,
+      port
+    }
+  })
+}
+
+/**
+ * DNS瑙f瀽娴嬭瘯
+ * @param {String} hostname - 涓绘満鍚�
+ */
+export function testDnsResolution(hostname) {
+  return request({
+    url: '/system/diag/testDns',
+    method: 'post',
+    data: {
+      hostname
+    }
+  })
+}
\ No newline at end of file
diff --git a/ruoyi-ui/src/api/system/ocr.js b/ruoyi-ui/src/api/system/ocr.js
new file mode 100644
index 0000000..98fba9c
--- /dev/null
+++ b/ruoyi-ui/src/api/system/ocr.js
@@ -0,0 +1,69 @@
+import request from '@/utils/request'
+
+/**
+ * 涓婁紶鍥剧墖杩涜OCR璇嗗埆
+ * @param {FormData} data - 鍖呭惈file銆乼ype鍜宲rovider鐨勮〃鍗曟暟鎹�
+ */
+export function recognizeImage(data) {
+  return request({
+    url: '/system/ocr/recognize',
+    method: 'post',
+    data: data,
+    headers: {
+      'Content-Type': 'multipart/form-data',
+      'repeatSubmit': false  // 绂佺敤闃查噸澶嶆彁浜ゆ鏌�
+    }
+  })
+}
+
+/**
+ * 鑾峰彇鏀寔鐨凮CR璇嗗埆绫诲瀷
+ */
+export function getOcrTypes() {
+  return request({
+    url: '/system/ocr/types',
+    method: 'get'
+  })
+}
+
+/**
+ * 鑾峰彇鏀寔鐨凮CR鏈嶅姟鎻愪緵鍟�
+ */
+export function getOcrProviders() {
+  return request({
+    url: '/system/ocr/providers',
+    method: 'get'
+  })
+}
+
+/**
+ * 閫氳繃URL杩涜OCR璇嗗埆
+ * @param {String} imageUrl - 鍥剧墖URL鍦板潃
+ * @param {String} type - 璇嗗埆绫诲瀷
+ * @param {String} provider - OCR鏈嶅姟鎻愪緵鍟�
+ * @param {Array} itemNames - 闇�瑕佹彁鍙栫殑瀛楁鍚嶇О鏁扮粍
+ */
+export function recognizeByUrl(imageUrl, type, provider, itemNames) {
+  return request({
+    url: '/system/ocr/recognizeByUrl',
+    method: 'get',
+    params: {
+      imageUrl: imageUrl,
+      type: type || 'General',
+      provider: provider || 'ali',
+      itemNames: itemNames
+    }
+  })
+}
+
+/**
+ * 鎻愬彇OCR缁撴灉涓殑鐩爣瀛楁
+ * @param {Object} ocrResult - OCR鍘熷缁撴灉
+ */
+export function extractFields(ocrResult) {
+  return request({
+    url: '/system/ocr/extractFields',
+    method: 'post',
+    data: ocrResult
+  })
+}
\ No newline at end of file
diff --git a/ruoyi-ui/src/api/system/vehicle.js b/ruoyi-ui/src/api/system/vehicle.js
index 56f0627..edae927 100644
--- a/ruoyi-ui/src/api/system/vehicle.js
+++ b/ruoyi-ui/src/api/system/vehicle.js
@@ -50,4 +50,4 @@
     method: 'get',
     params: query
   })
-} 
\ No newline at end of file
+}
diff --git a/ruoyi-ui/src/api/system/vehicleAlert.js b/ruoyi-ui/src/api/system/vehicleAlert.js
new file mode 100644
index 0000000..4e985f6
--- /dev/null
+++ b/ruoyi-ui/src/api/system/vehicleAlert.js
@@ -0,0 +1,80 @@
+import request from '@/utils/request'
+
+// 鏌ヨ杞﹁締寮傚父鍛婅鍒楄〃
+export function listVehicleAlert(query) {
+  return request({
+    url: '/system/vehicleAlert/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鏌ヨ杞﹁締寮傚父鍛婅璇︾粏
+export function getVehicleAlert(alertId) {
+  return request({
+    url: '/system/vehicleAlert/' + alertId,
+    method: 'get'
+  })
+}
+
+// 鏂板杞﹁締寮傚父鍛婅
+export function addVehicleAlert(data) {
+  return request({
+    url: '/system/vehicleAlert',
+    method: 'post',
+    data: data
+  })
+}
+
+// 淇敼杞﹁締寮傚父鍛婅
+export function updateVehicleAlert(data) {
+  return request({
+    url: '/system/vehicleAlert',
+    method: 'put',
+    data: data
+  })
+}
+
+// 鍒犻櫎杞﹁締寮傚父鍛婅
+export function delVehicleAlert(alertId) {
+  return request({
+    url: '/system/vehicleAlert/' + alertId,
+    method: 'delete'
+  })
+}
+
+// 澶勭悊鍛婅
+export function handleAlert(alertId, data) {
+  return request({
+    url: '/system/vehicleAlert/handle/' + alertId,
+    method: 'put',
+    data: data
+  })
+}
+
+// 鎵归噺澶勭悊鍛婅
+export function batchHandleAlert(alertIds, data) {
+  return request({
+    url: '/system/vehicleAlert/batchHandle',
+    method: 'put',
+    params: { alertIds: alertIds.join(',') },
+    data: data
+  })
+}
+
+// 鑾峰彇鏈鐞嗗憡璀︽暟閲�
+export function getUnhandledCount() {
+  return request({
+    url: '/system/vehicleAlert/unhandledCount',
+    method: 'get'
+  })
+}
+
+// 瀵煎嚭杞﹁締寮傚父鍛婅
+export function exportVehicleAlert(query) {
+  return request({
+    url: '/system/vehicleAlert/export',
+    method: 'get',
+    params: query
+  })
+}
diff --git a/ruoyi-ui/src/api/system/vehicleAlertConfig.js b/ruoyi-ui/src/api/system/vehicleAlertConfig.js
new file mode 100644
index 0000000..a92d123
--- /dev/null
+++ b/ruoyi-ui/src/api/system/vehicleAlertConfig.js
@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 鏌ヨ杞﹁締鍛婅閰嶇疆鍒楄〃
+export function listVehicleAlertConfig(query) {
+  return request({
+    url: '/system/vehicleAlertConfig/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鏌ヨ杞﹁締鍛婅閰嶇疆璇︾粏
+export function getVehicleAlertConfig(configId) {
+  return request({
+    url: '/system/vehicleAlertConfig/' + configId,
+    method: 'get'
+  })
+}
+
+// 鏂板杞﹁締鍛婅閰嶇疆
+export function addVehicleAlertConfig(data) {
+  return request({
+    url: '/system/vehicleAlertConfig',
+    method: 'post',
+    data: data
+  })
+}
+
+// 淇敼杞﹁締鍛婅閰嶇疆
+export function updateVehicleAlertConfig(data) {
+  return request({
+    url: '/system/vehicleAlertConfig',
+    method: 'put',
+    data: data
+  })
+}
+
+// 鍒犻櫎杞﹁締鍛婅閰嶇疆
+export function delVehicleAlertConfig(configId) {
+  return request({
+    url: '/system/vehicleAlertConfig/' + configId,
+    method: 'delete'
+  })
+}
+
+// 瀵煎嚭杞﹁締鍛婅閰嶇疆
+export function exportVehicleAlertConfig(query) {
+  return request({
+    url: '/system/vehicleAlertConfig/export',
+    method: 'get',
+    params: query
+  })
+}
diff --git a/ruoyi-ui/src/api/system/vehicleSync.js b/ruoyi-ui/src/api/system/vehicleSync.js
new file mode 100644
index 0000000..44dd199
--- /dev/null
+++ b/ruoyi-ui/src/api/system/vehicleSync.js
@@ -0,0 +1,18 @@
+import request from '@/utils/request'
+
+// 鏌ヨ杞﹁締鍚屾鍒楄〃
+export function listVehicleSync() {
+  return request({
+    url: '/system/vehicleSync/list',
+    method: 'get'
+  })
+}
+
+// 鎵嬪姩鍚屾杞﹁締
+export function syncVehicle(data) {
+  return request({
+    url: '/system/vehicleSync/syncVehicle',
+    method: 'post',
+    data: data
+  })
+}
diff --git a/ruoyi-ui/src/router/index.js b/ruoyi-ui/src/router/index.js
index 81cbe25..6266b3e 100644
--- a/ruoyi-ui/src/router/index.js
+++ b/ruoyi-ui/src/router/index.js
@@ -151,6 +151,57 @@
 // 鍔ㄦ�佽矾鐢憋紝鍩轰簬鐢ㄦ埛鏉冮檺鍔ㄦ�佸幓鍔犺浇
 export const dynamicRoutes = [
   qywechatRouter,
+  
+  // 缃戠粶璇婃柇璺敱
+  {
+    path: "/system/diag",
+    component: Layout,
+    redirect: "/system/diag/ocrConnection",
+    name: "Diag",
+    meta: {
+      title: "缃戠粶璇婃柇",
+      icon: "link",
+      permissions: ["system:diag:view"]
+    },
+    children: [
+      {
+        path: "ocrConnection",
+        component: () => import("@/views/system/diag/ocrConnection"),
+        name: "OCRConnectionDiag",
+        meta: {
+          title: "OCR杩炴帴璇婃柇",
+          icon: "monitor",
+          permissions: ["system:diag:ocr"]
+        }
+      }
+    ]
+  },
+  
+  // 鍖婚櫌鍒嗚瘝娴嬭瘯璺敱
+  {
+    path: "/system/hospital",
+    component: Layout,
+    redirect: "/system/hospital/tokenizer",
+    name: "Hospital",
+    meta: {
+      title: "鍖婚櫌绠$悊",
+      icon: "hospital",
+      permissions: ["system:hospital:view"]
+    },
+    children: [
+      {
+        path: "tokenizer",
+        component: () => import("@/views/system/hospital/tokenizer"),
+        name: "HospitalTokenizer",
+        meta: {
+          title: "鍖婚櫌鍒嗚瘝娴嬭瘯",
+          icon: "search",
+          permissions: ["system:hospital:tokenizer"]
+        }
+      }
+    ]
+  },
+  
   {
     path: '/system/user-auth',
     component: Layout,
diff --git a/ruoyi-ui/src/views/system/diag/ocrConnection.vue b/ruoyi-ui/src/views/system/diag/ocrConnection.vue
new file mode 100644
index 0000000..f081e10
--- /dev/null
+++ b/ruoyi-ui/src/views/system/diag/ocrConnection.vue
@@ -0,0 +1,261 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-card">
+      <div slot="header" class="clearfix">
+        <span>OCR鏈嶅姟杩炴帴璇婃柇</span>
+      </div>
+
+      <el-alert
+        title="璇婃柇璇存槑"
+        type="info"
+        :closable="false"
+        style="margin-bottom: 20px"
+      >
+        <div>
+          鏈〉闈㈢敤浜庤瘖鏂璒CR鏈嶅姟杩炴帴鐘舵�侊紝鍖呮嫭DNS瑙f瀽銆佺綉缁滆繛閫氭�х瓑銆�<br/>
+          <strong>璇婃柇鍐呭锛�</strong>DNS瑙f瀽娴嬭瘯銆佽繛鎺ユ祴璇曘�佺綉缁滈厤缃鏌�
+        </div>
+      </el-alert>
+
+      <div style="text-align: center; margin-bottom: 30px;">
+        <el-button
+          type="primary"
+          :loading="diagnosing"
+          @click="handleDiagnosis"
+        >
+          <i class="el-icon-search"></i> 寮�濮嬭瘖鏂�
+        </el-button>
+      </div>
+
+      <!-- 璇婃柇缁撴灉 -->
+      <div v-if="diagnosisResult">
+        <!-- DNS瑙f瀽缁撴灉 -->
+        <el-card shadow="never" class="result-card">
+          <div slot="header">
+            <span>DNS瑙f瀽娴嬭瘯</span>
+            <el-tag 
+              :type="diagnosisResult.dns.success ? 'success' : 'danger'" 
+              style="float: right; margin-top: 5px;"
+            >
+              {{ diagnosisResult.dns.success ? '鉁� 鎴愬姛' : '鉂� 澶辫触' }}
+            </el-tag>
+          </div>
+          
+          <div class="result-content">
+            <p><strong>鍩熷悕锛�</strong>{{ ocrEndpoint }}</p>
+            <p v-if="diagnosisResult.dns.ips"><strong>IP鍦板潃锛�</strong>{{ diagnosisResult.dns.ips.join(', ') }}</p>
+            <p v-if="diagnosisResult.dns.ipCount"><strong>瑙f瀽鏁伴噺锛�</strong>{{ diagnosisResult.dns.ipCount }}涓�</p>
+            <p v-if="diagnosisResult.dns.error"><strong>閿欒淇℃伅锛�</strong>{{ diagnosisResult.dns.error }}</p>
+            
+            <div v-if="!diagnosisResult.dns.success" style="margin-top: 15px;">
+              <el-alert
+                title="DNS瑙f瀽寤鸿"
+                type="warning"
+                :closable="false"
+                show-icon
+              >
+                <ul style="margin: 0; padding-left: 20px;">
+                  <li>妫�鏌NS鏈嶅姟鍣ㄩ厤缃�</li>
+                  <li>灏濊瘯浣跨敤鍏叡DNS锛堝8.8.8.8鎴�114.114.114.114锛�</li>
+                  <li>纭鍩熷悕鏄惁姝g‘锛歿{ ocrEndpoint }}</li>
+                </ul>
+              </el-alert>
+            </div>
+          </div>
+        </el-card>
+
+        <!-- 杩炴帴娴嬭瘯缁撴灉 -->
+        <el-card shadow="never" class="result-card">
+          <div slot="header">
+            <span>杩炴帴娴嬭瘯</span>
+            <el-tag 
+              :type="diagnosisResult.connection.success ? 'success' : 'danger'" 
+              style="float: right; margin-top: 5px;"
+            >
+              {{ diagnosisResult.connection.success ? '鉁� 鎴愬姛' : '鉂� 澶辫触' }}
+            </el-tag>
+          </div>
+          
+          <div class="result-content">
+            <p><strong>娴嬭瘯鍦板潃锛�</strong>{{ ocrEndpoint }}:{{ ocrPort }}</p>
+            <p v-if="diagnosisResult.connection.responseTime"><strong>鍝嶅簲鏃堕棿锛�</strong>{{ diagnosisResult.connection.responseTime }}</p>
+            <p v-if="diagnosisResult.connection.error"><strong>閿欒淇℃伅锛�</strong>{{ diagnosisResult.connection.error }}</p>
+            
+            <div v-if="!diagnosisResult.connection.success" style="margin-top: 15px;">
+              <el-alert
+                title="杩炴帴娴嬭瘯寤鸿"
+                type="warning"
+                :closable="false"
+                show-icon
+              >
+                <ul style="margin: 0; padding-left: 20px;">
+                  <li>妫�鏌ラ槻鐏鏄惁寮�鏀緖{ ocrPort }}绔彛</li>
+                  <li>纭鏈嶅姟鍣ㄥ厑璁稿嚭绔橦TTPS璇锋眰</li>
+                  <li>妫�鏌ョ綉缁滅瓥鐣ユ槸鍚﹀厑璁歌闂閮ㄦ湇鍔�</li>
+                  <li>楠岃瘉浠g悊閰嶇疆锛堝閫傜敤锛�</li>
+                </ul>
+              </el-alert>
+            </div>
+          </div>
+        </el-card>
+
+        <!-- 缃戠粶閰嶇疆 -->
+        <el-card shadow="never" class="result-card">
+          <div slot="header">
+            <span>缃戠粶閰嶇疆</span>
+          </div>
+          
+          <div class="result-content">
+            <p><strong>HTTP浠g悊锛�</strong>{{ diagnosisResult.network.httpProxy || '鏃�' }}</p>
+            <p><strong>HTTPS浠g悊锛�</strong>{{ diagnosisResult.network.httpsProxy || '鏃�' }}</p>
+            <p><strong>鏈湴鍦板潃锛�</strong>{{ diagnosisResult.network.localHostAddress || '-' }}</p>
+            <p><strong>鏈湴涓绘満鍚嶏細</strong>{{ diagnosisResult.network.localHostName || '-' }}</p>
+          </div>
+        </el-card>
+
+        <!-- 璇婃柇寤鸿 -->
+        <el-card shadow="never" class="result-card">
+          <div slot="header">
+            <span>璇婃柇寤鸿</span>
+          </div>
+          
+          <div class="result-content">
+            <div v-if="overallStatus === 'success'">
+              <el-alert
+                title="璇婃柇缁撴灉锛氫竴鍒囨甯�"
+                type="success"
+                :closable="false"
+                show-icon
+              >
+                <p>OCR鏈嶅姟杩炴帴姝e父锛屾偍鍙互姝e父浣跨敤OCR鍔熻兘銆�</p>
+              </el-alert>
+            </div>
+            <div v-else-if="overallStatus === 'warning'">
+              <el-alert
+                title="璇婃柇缁撴灉锛氶儴鍒嗛棶棰�"
+                type="warning"
+                :closable="false"
+                show-icon
+              >
+                <p>瀛樺湪閮ㄥ垎缃戠粶闂锛屽彲鑳藉奖鍝峅CR鏈嶅姟浣跨敤銆�</p>
+              </el-alert>
+            </div>
+            <div v-else>
+              <el-alert
+                title="璇婃柇缁撴灉锛氬瓨鍦ㄩ棶棰�"
+                type="error"
+                :closable="false"
+                show-icon
+              >
+                <p>缃戠粶杩炴帴瀛樺湪闂锛岄渶瑕佷慨澶嶅悗鎵嶈兘浣跨敤OCR鏈嶅姟銆�</p>
+              </el-alert>
+            </div>
+            
+            <h4 style="margin: 15px 0 10px 0;">瑙e喅鏂规锛�</h4>
+            <ol>
+              <li><strong>妫�鏌ョ綉缁滆繛鎺�</strong> - 纭繚鏈嶅姟鍣ㄥ彲浠ヨ闂缃�</li>
+              <li><strong>楠岃瘉DNS瑙f瀽</strong> - 纭鍩熷悕 {{ ocrEndpoint }} 鑳藉姝g‘瑙f瀽</li>
+              <li><strong>妫�鏌ラ槻鐏璁剧疆</strong> - 纭繚{{ ocrPort }}绔彛寮�鏀�</li>
+              <li><strong>閰嶇疆浠g悊锛堝闇�瑕侊級</strong> - 濡傛灉閫氳繃浠g悊璁块棶锛屾鏌ヤ唬鐞嗛厤缃�</li>
+              <li><strong>楠岃瘉闃块噷浜戞湇鍔�</strong> - 纭OCR鏈嶅姟宸插紑閫氫笖AccessKey鏈夋晥</li>
+            </ol>
+          </div>
+        </el-card>
+      </div>
+
+      <!-- 璇婃柇涓� -->
+      <div v-if="diagnosing" style="text-align: center; padding: 50px 0">
+        <i class="el-icon-loading" style="font-size: 40px; color: #409EFF"></i>
+        <p style="margin-top: 20px; color: #909399">姝e湪璇婃柇缃戠粶杩炴帴锛岃绋嶅��...</p>
+        <p style="color: #c0c4cc; font-size: 12px;">杩欏彲鑳介渶瑕佸嚑绉掗挓鏃堕棿</p>
+      </div>
+
+      <!-- 鏈紑濮嬭瘖鏂� -->
+      <div v-else-if="!diagnosisResult" style="text-align: center; padding: 50px 0; color: #909399">
+        <i class="el-icon-monitor" style="font-size: 60px"></i>
+        <p style="margin-top: 20px">鐐瑰嚮"寮�濮嬭瘖鏂�"鎸夐挳妫�娴婳CR鏈嶅姟杩炴帴鐘舵��</p>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import { diagOcrConnection } from "@/api/system/networkDiag";
+
+export default {
+  name: "OCRConnectionDiag",
+  data() {
+    return {
+      diagnosing: false,
+      diagnosisResult: null,
+      ocrEndpoint: 'ocr-api.cn-hangzhou.aliyuncs.com',
+      ocrPort: 443
+    };
+  },
+  computed: {
+    overallStatus() {
+      if (!this.diagnosisResult) return null;
+      
+      const { dns, connection } = this.diagnosisResult;
+      
+      if (dns.success && connection.success) {
+        return 'success';
+      } else if (!dns.success || !connection.success) {
+        return 'error';
+      }
+      
+      return 'warning';
+    }
+  },
+  methods: {
+    /** 寮�濮嬭瘖鏂� */
+    handleDiagnosis() {
+      this.diagnosing = true;
+      this.diagnosisResult = null;
+      
+      diagOcrConnection().then(response => {
+        this.diagnosisResult = response.data;
+        this.diagnosing = false;
+      }).catch(error => {
+        this.$modal.msgError('缃戠粶璇婃柇澶辫触: ' + error.message);
+        this.diagnosing = false;
+      });
+    }
+  }
+};
+</script>
+
+<style scoped>
+.result-card {
+  margin-bottom: 20px;
+}
+
+.result-content p {
+  margin: 8px 0;
+  line-height: 1.5;
+}
+
+.result-content ul {
+  margin: 10px 0;
+  padding-left: 20px;
+}
+
+.result-content ol {
+  margin: 10px 0;
+  padding-left: 20px;
+}
+
+.result-content li {
+  margin: 5px 0;
+  line-height: 1.5;
+}
+
+.box-card {
+  margin-bottom: 20px;
+}
+
+h4 {
+  margin: 15px 0 10px 0;
+  color: #303133;
+}
+</style>
\ No newline at end of file
diff --git a/ruoyi-ui/src/views/system/hospital/tokenizer.vue b/ruoyi-ui/src/views/system/hospital/tokenizer.vue
new file mode 100644
index 0000000..dddcbe1
--- /dev/null
+++ b/ruoyi-ui/src/views/system/hospital/tokenizer.vue
@@ -0,0 +1,418 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-card" shadow="hover">
+      <div slot="header" class="clearfix">
+        <span style="font-weight: bold; font-size: 16px;">鍖婚櫌鍒嗚瘝绠$悊涓庢祴璇曞伐鍏�</span>
+        <el-tag type="info" size="small" style="margin-left: 10px;">浣跨敤 HanLP 涓撲笟涓枃鍒嗚瘝</el-tag>
+      </div>
+
+      <!-- 鎵归噺鍒嗚瘝鍖哄煙 -->
+      <el-divider content-position="left">
+        <i class="el-icon-setting"></i> 鎵归噺鍒嗚瘝绠$悊
+      </el-divider>
+      
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-alert
+            title="鎻愮ず"
+            type="info"
+            :closable="false"
+            style="margin-bottom: 15px;">
+            <template slot>
+              鎵归噺涓烘墍鏈夊尰闄㈢敓鎴愬垎璇嶆暟鎹�傞娆¢儴缃叉椂蹇呴』鎵ц涓�娆★紝鎴栧湪鍒嗚瘝绠楁硶鏇存柊鍚庨噸鏂扮敓鎴愩��
+            </template>
+          </el-alert>
+          
+          <el-button
+            type="primary"
+            icon="el-icon-cpu"
+            :loading="generating"
+            @click="handleGenerateKeywords"
+            size="medium">
+            {{ generating ? '姝e湪鐢熸垚鍒嗚瘝...' : '鎵归噺鐢熸垚鎵�鏈夊尰闄㈠垎璇�' }}
+          </el-button>
+          
+          <el-button
+            type="success"
+            icon="el-icon-refresh"
+            @click="resetStatus"
+            size="medium"
+            v-if="generateResult">
+            閲嶇疆
+          </el-button>
+          
+          <!-- 杩涘害鏉� -->
+          <div v-if="generating && taskProgress" style="margin-top: 20px;">
+            <el-progress
+              :percentage="taskProgress.progress || 0"
+              :status="taskProgress.status === 'FAILED' ? 'exception' : null">
+            </el-progress>
+            <div style="margin-top: 10px; color: #606266; font-size: 14px;">
+              <span>杩涘害: {{ taskProgress.processedCount || 0 }} / {{ taskProgress.totalCount || 0 }}</span>
+              <span style="margin-left: 20px;">鎴愬姛: {{ taskProgress.successCount || 0 }}</span>
+              <span style="margin-left: 20px;">澶辫触: {{ taskProgress.failedCount || 0 }}</span>
+            </div>
+          </div>
+          
+          <div v-if="generateResult" style="margin-top: 15px;">
+            <el-result
+              :icon="generateResult.success ? 'success' : 'error'"
+              :title="generateResult.title"
+              :subTitle="generateResult.message">
+            </el-result>
+          </div>
+        </el-col>
+      </el-row>
+
+      <!-- 鍒嗚瘝娴嬭瘯鍖哄煙 -->
+      <el-divider content-position="left">
+        <i class="el-icon-search"></i> 鍖婚櫌鍖归厤娴嬭瘯
+      </el-divider>
+      
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-alert
+            title="娴嬭瘯璇存槑"
+            type="success"
+            :closable="false"
+            style="margin-bottom: 15px;">
+            <template slot>
+              杈撳叆鍖婚櫌鍚嶇О銆佸湴鍧�鎴栧叧閿瘝锛岀郴缁熷皢鑷姩杩涜鍒嗚瘝骞跺尮閰嶇浉浼肩殑鍖婚櫌銆傚尮閰嶇粨鏋滄寜鐩稿叧搴︽帓搴忋��
+            </template>
+          </el-alert>
+          
+          <el-form :model="searchForm" label-width="100px">
+            <el-form-item label="鎼滅储鏂囨湰">
+              <el-input
+                v-model="searchForm.searchText"
+                placeholder="璇疯緭鍏ュ尰闄㈠悕绉般�佸湴鍧�鎴栧叧閿瘝锛屼緥濡傦細鍖椾含鍗忓拰鍖婚櫌銆佷笂娴风憺閲�"
+                clearable
+                @keyup.enter.native="handleSearch"
+                style="width: 60%;">
+                <el-button
+                  slot="append"
+                  icon="el-icon-search"
+                  @click="handleSearch"
+                  :loading="searching">
+                  鎼滅储
+                </el-button>
+              </el-input>
+              
+              <el-input-number
+                v-model="searchForm.pageSize"
+                :min="5"
+                :max="100"
+                :step="5"
+                controls-position="right"
+                style="width: 150px; margin-left: 10px;">
+              </el-input-number>
+              <span style="margin-left: 5px; color: #909399;">鏉$粨鏋�</span>
+            </el-form-item>
+            
+            <el-form-item label="鍒嗚瘝缁撴灉" v-if="tokenizedKeywords">
+              <el-tag
+                v-for="(keyword, index) in tokenizedKeywords.split(',')"
+                :key="index"
+                type="success"
+                size="small"
+                style="margin-right: 5px; margin-bottom: 5px;">
+                {{ keyword }}
+              </el-tag>
+            </el-form-item>
+          </el-form>
+          
+          <!-- 鎼滅储缁撴灉琛ㄦ牸 -->
+          <el-table
+            v-loading="searching"
+            :data="searchResults"
+            border
+            stripe
+            style="width: 100%; margin-top: 10px;"
+            :height="400"
+            v-if="searchResults.length > 0">
+            <el-table-column type="index" label="鎺掑悕" width="60" align="center" />
+            <el-table-column prop="matchScore" label="鍖归厤鍒嗘暟" width="100" align="center" sortable>
+              <template slot-scope="scope">
+                <el-tag :type="getScoreType(scope.row.matchScore)" size="medium">
+                  {{ scope.row.matchScore }}
+                </el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column prop="hospital.hospId" label="鍖婚櫌ID" width="80" align="center" />
+            <el-table-column prop="hospital.hospName" label="鍖婚櫌鍚嶇О" min-width="180" show-overflow-tooltip />
+            <el-table-column prop="hospital.hospShort" label="绠�绉�" width="120" show-overflow-tooltip />
+            <el-table-column label="鍦板潃" min-width="220" show-overflow-tooltip>
+              <template slot-scope="scope">
+                {{ formatAddress(scope.row.hospital) }}
+              </template>
+            </el-table-column>
+            <el-table-column prop="hospital.hospTel" label="鐢佃瘽" width="130" />
+            <el-table-column label="鐘舵��" width="80" align="center">
+              <template slot-scope="scope">
+                <el-tag v-if="scope.row.hospital.hospState === 1" type="success" size="small">姝e父</el-tag>
+                <el-tag v-else type="info" size="small">鏈煡</el-tag>
+              </template>
+            </el-table-column>
+          </el-table>
+          
+          <!-- 鏃犵粨鏋滄彁绀� -->
+          <el-empty
+            v-if="searched && searchResults.length === 0"
+            description="鏈壘鍒板尮閰嶇殑鍖婚櫌"
+            :image-size="100">
+          </el-empty>
+          
+          <!-- 缁熻淇℃伅 -->
+          <div v-if="searchResults.length > 0" style="margin-top: 10px; color: #909399;">
+            <i class="el-icon-info"></i>
+            鍏辨壘鍒� <span style="color: #409EFF; font-weight: bold;">{{ searchResults.length }}</span> 涓尮閰嶇殑鍖婚櫌
+          </div>
+        </el-col>
+      </el-row>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import request from '@/utils/request'
+
+export default {
+  name: 'HospitalTokenizer',
+  data() {
+    return {
+      // 鎵归噺鐢熸垚鐘舵��
+      generating: false,
+      generateResult: null,
+      currentTaskId: null,
+      taskProgress: null,
+      progressTimer: null,
+      
+      // 鎼滅储琛ㄥ崟
+      searchForm: {
+        searchText: '',
+        pageSize: 30
+      },
+      
+      // 鎼滅储鐘舵��
+      searching: false,
+      searched: false,
+      tokenizedKeywords: '',
+      searchResults: []
+    }
+  },
+  methods: {
+    /** 鎵归噺鐢熸垚鍒嗚瘝 */
+    handleGenerateKeywords() {
+      this.$confirm('纭瑕佷负鎵�鏈夊尰闄㈢敓鎴愬垎璇嶅悧锛熻繖鍙兘闇�瑕佸嚑鍒嗛挓鏃堕棿銆�', '纭鎿嶄綔', {
+        confirmButtonText: '纭畾',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning'
+      }).then(() => {
+        this.generating = true
+        this.generateResult = null
+        this.taskProgress = null
+        
+        request({
+          url: '/system/hospital/generateKeywords',
+          method: 'get'
+        }).then(response => {
+          if (response.code === 200) {
+            // 鑾峰彇浠诲姟ID
+            this.currentTaskId = response.data.taskId
+            this.$message.success('鍒嗚瘝浠诲姟宸插惎鍔紝姝e湪鍚庡彴鎵ц...')
+            
+            // 寮�濮嬭疆璇换鍔¤繘搴�
+            this.startProgressPolling()
+          } else {
+            this.generating = false
+            this.$message.error(response.msg || '浠诲姟鍚姩澶辫触')
+          }
+        }).catch(error => {
+          this.generating = false
+          this.$message.error('缃戠粶璇锋眰澶辫触')
+          console.error('鐢熸垚鍒嗚瘝澶辫触:', error)
+        })
+      }).catch(() => {
+        this.$message.info('宸插彇娑堟搷浣�')
+      })
+    },
+    
+    /** 寮�濮嬭疆璇换鍔¤繘搴� */
+    startProgressPolling() {
+      this.queryTaskProgress()
+      
+      // 姣�2绉掕疆璇竴娆�
+      this.progressTimer = setInterval(() => {
+        this.queryTaskProgress()
+      }, 2000)
+    },
+    
+    /** 鏌ヨ浠诲姟杩涘害 */
+    queryTaskProgress() {
+      if (!this.currentTaskId) {
+        return
+      }
+      
+      request({
+        url: '/system/hospital/getTaskProgress',
+        method: 'get',
+        params: {
+          taskId: this.currentTaskId
+        }
+      }).then(response => {
+        if (response.code === 200) {
+          this.taskProgress = response.data
+          
+          // 鍒ゆ柇浠诲姟鏄惁瀹屾垚
+          if (this.taskProgress.status === 'SUCCESS') {
+            this.stopProgressPolling()
+            this.generating = false
+            this.generateResult = {
+              success: true,
+              title: '鐢熸垚鎴愬姛',
+              message: `鍏卞鐞� ${this.taskProgress.totalCount} 涓尯闄紝鎴愬姛 ${this.taskProgress.successCount} 涓紝澶辫触 ${this.taskProgress.failedCount} 涓猔
+            }
+            this.$message.success('鍖婚櫌鍒嗚瘝鐢熸垚瀹屾垚锛�')
+          } else if (this.taskProgress.status === 'FAILED') {
+            this.stopProgressPolling()
+            this.generating = false
+            this.generateResult = {
+              success: false,
+              title: '鐢熸垚澶辫触',
+              message: this.taskProgress.errorMessage || '浠诲姟鎵ц澶辫触'
+            }
+            this.$message.error('鍖婚櫌鍒嗚瘝鐢熸垚澶辫触锛�')
+          }
+        } else {
+          // 浠诲姟涓嶅瓨鍦ㄦ垨宸茶繃鏈�
+          this.stopProgressPolling()
+          this.generating = false
+        }
+      }).catch(error => {
+        console.error('鏌ヨ杩涘害澶辫触:', error)
+      })
+    },
+    
+    /** 鍋滄杞 */
+    stopProgressPolling() {
+      if (this.progressTimer) {
+        clearInterval(this.progressTimer)
+        this.progressTimer = null
+      }
+    },
+    
+    /** 閲嶇疆鐘舵�� */
+    resetStatus() {
+      this.generateResult = null
+      this.taskProgress = null
+      this.currentTaskId = null
+      this.stopProgressPolling()
+    },
+    
+    /** 鎼滅储鍖婚櫌 */
+    handleSearch() {
+      if (!this.searchForm.searchText.trim()) {
+        this.$message.warning('璇疯緭鍏ユ悳绱㈡枃鏈�')
+        return
+      }
+      
+      this.searching = true
+      this.searched = false
+      this.tokenizedKeywords = ''
+      this.searchResults = []
+      
+      request({
+        url: '/system/hospital/searchByKeywords',
+        method: 'get',
+        params: {
+          searchText: this.searchForm.searchText,
+          pageSize: this.searchForm.pageSize
+        }
+      }).then(response => {
+        this.searching = false
+        this.searched = true
+        
+        if (response.code === 200) {
+          this.searchResults = response.data || []
+          
+          // 鎻愬彇鍒嗚瘝缁撴灉锛堜粠鏃ュ織涓級
+          if (this.searchResults.length > 0) {
+            this.$message.success(`鎵惧埌 ${this.searchResults.length} 涓尮閰嶇殑鍖婚櫌`)
+            // 妯℃嫙鏄剧ず鍒嗚瘝缁撴灉
+            this.tokenizedKeywords = this.generateMockKeywords(this.searchForm.searchText)
+          } else {
+            this.$message.info('鏈壘鍒板尮閰嶇殑鍖婚櫌锛岃灏濊瘯鍏朵粬鍏抽敭璇�')
+          }
+        } else {
+          this.$message.error(response.msg || '鎼滅储澶辫触')
+        }
+      }).catch(error => {
+        this.searching = false
+        this.searched = true
+        this.$message.error('鎼滅储澶辫触锛�' + (error.message || '缃戠粶閿欒'))
+        console.error('鎼滅储澶辫触:', error)
+      })
+    },
+    
+    /** 鏍煎紡鍖栧湴鍧� */
+    formatAddress(row) {
+      const parts = []
+      if (row.hopsProvince) parts.push(row.hopsProvince)
+      if (row.hopsCity) parts.push(row.hopsCity)
+      if (row.hopsArea) parts.push(row.hopsArea)
+      if (row.hospAddress) parts.push(row.hospAddress)
+      return parts.join(' ')
+    },
+    
+    /** 鑾峰彇鍒嗘暟鏍囩绫诲瀷 */
+    getScoreType(score) {
+      if (score >= 10) return 'danger'  // 楂樺尮閰� - 绾㈣壊
+      if (score >= 5) return 'warning'   // 涓尮閰� - 姗欒壊
+      if (score >= 3) return 'success'   // 浣庡尮閰� - 缁胯壊
+      return ''                           // 鏋佷綆鍖归厤 - 榛樿
+    },
+    
+    /** 鐢熸垚妯℃嫙鍒嗚瘝缁撴灉锛堢敤浜庡睍绀猴級 */
+    generateMockKeywords(text) {
+      // 杩欓噷绠�鍗曟ā鎷燂紝瀹為檯鍒嗚瘝鍦ㄥ悗绔畬鎴�
+      const keywords = []
+      for (let i = 0; i < text.length; i++) {
+        for (let len = 2; len <= Math.min(4, text.length - i); len++) {
+          keywords.push(text.substr(i, len))
+        }
+      }
+      return keywords.slice(0, 15).join(',')
+    }
+  },
+  
+  beforeDestroy() {
+    // 缁勪欢閿�姣佹椂鍋滄杞
+    this.stopProgressPolling()
+  }
+}
+</script>
+
+<style scoped>
+.box-card {
+  margin: 20px;
+}
+
+.clearfix:before,
+.clearfix:after {
+  display: table;
+  content: "";
+}
+
+.clearfix:after {
+  clear: both;
+}
+
+.el-divider {
+  margin: 30px 0 20px 0;
+}
+
+.el-divider__text {
+  font-weight: bold;
+  font-size: 14px;
+}
+</style>
diff --git a/ruoyi-ui/src/views/system/ocr/config.vue b/ruoyi-ui/src/views/system/ocr/config.vue
new file mode 100644
index 0000000..6b5fe9e
--- /dev/null
+++ b/ruoyi-ui/src/views/system/ocr/config.vue
@@ -0,0 +1,277 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-card">
+      <div slot="header" class="clearfix">
+        <span>OCR鏈嶅姟閰嶇疆</span>
+      </div>
+
+      <el-tabs v-model="activeTab">
+        <el-tab-pane label="闃块噷浜慜CR閰嶇疆" name="ali">
+          <el-alert
+            title="闃块噷浜慜CR閰嶇疆璇存槑"
+            type="info"
+            :closable="false"
+            style="margin-bottom: 20px"
+          >
+            <div>
+              閰嶇疆闃块噷浜慜CR鏈嶅姟鐨凙ccessKey淇℃伅銆傞厤缃悗閲嶅惎搴旂敤鐢熸晥銆�<br/>
+              <strong>娉ㄦ剰锛�</strong>璇峰Ε鍠勪繚绠ccessKey锛岄伩鍏嶆硠闇�
+            </div>
+          </el-alert>
+
+          <el-form
+            :model="aliForm"
+            :rules="rules"
+            ref="aliForm"
+            label-width="120px"
+            style="max-width: 600px;"
+          >
+            <el-form-item label="AccessKey ID" prop="accessKeyId">
+              <el-input
+                v-model="aliForm.accessKeyId"
+                placeholder="璇疯緭鍏ラ樋閲屼簯AccessKey ID"
+                show-password
+                :disabled="!canEditAli"
+              />
+            </el-form-item>
+
+            <el-form-item label="AccessKey Secret" prop="accessKeySecret">
+              <el-input
+                v-model="aliForm.accessKeySecret"
+                placeholder="璇疯緭鍏ラ樋閲屼簯AccessKey Secret"
+                show-password
+                :disabled="!canEditAli"
+              />
+            </el-form-item>
+
+            <el-form-item label="OCR鏈嶅姟绔偣">
+              <el-input
+                v-model="aliOcrEndpoint"
+                :disabled="true"
+                placeholder="闃块噷浜慜CR鏈嶅姟绔偣"
+              />
+              <div style="margin-top: 5px; color: #909399; font-size: 12px;">
+                榛樿绔偣锛歿{ aliOcrEndpoint }}
+              </div>
+            </el-form-item>
+
+            <el-form-item>
+              <el-button
+                v-if="!canEditAli"
+                type="primary"
+                @click="handleEditAli"
+              >
+                <i class="el-icon-edit"></i> 缂栬緫閰嶇疆
+              </el-button>
+              <el-button
+                v-if="canEditAli"
+                type="success"
+                @click="handleSaveAli"
+              >
+                <i class="el-icon-check"></i> 淇濆瓨閰嶇疆
+              </el-button>
+              <el-button
+                v-if="canEditAli"
+                @click="handleCancelAli"
+              >
+                <i class="el-icon-close"></i> 鍙栨秷
+              </el-button>
+            </el-form-item>
+          </el-form>
+        </el-tab-pane>
+
+        <el-tab-pane label="鐧惧害OCR閰嶇疆" name="baidu">
+          <el-alert
+            title="鐧惧害OCR閰嶇疆璇存槑"
+            type="info"
+            :closable="false"
+            style="margin-bottom: 20px"
+          >
+            <div>
+              閰嶇疆鐧惧害AI寮�鏀惧钩鍙癘CR鏈嶅姟鐨凙ppID銆丄PI Key鍜孲ecret Key淇℃伅銆�<br/>
+              <strong>娉ㄦ剰锛�</strong>璇峰Ε鍠勪繚绠″瘑閽ヤ俊鎭紝閬垮厤娉勯湶
+            </div>
+          </el-alert>
+
+          <el-form
+            :model="baiduForm"
+            :rules="baiduRules"
+            ref="baiduForm"
+            label-width="120px"
+            style="max-width: 600px;"
+          >
+            <el-form-item label="App ID" prop="appId">
+              <el-input
+                v-model="baiduForm.appId"
+                placeholder="璇疯緭鍏ョ櫨搴CR App ID"
+                :disabled="!canEditBaidu"
+              />
+            </el-form-item>
+
+            <el-form-item label="API Key" prop="apiKey">
+              <el-input
+                v-model="baiduForm.apiKey"
+                placeholder="璇疯緭鍏ョ櫨搴CR API Key"
+                show-password
+                :disabled="!canEditBaidu"
+              />
+            </el-form-item>
+
+            <el-form-item label="Secret Key" prop="secretKey">
+              <el-input
+                v-model="baiduForm.secretKey"
+                placeholder="璇疯緭鍏ョ櫨搴CR Secret Key"
+                show-password
+                :disabled="!canEditBaidu"
+              />
+            </el-form-item>
+
+            <el-form-item>
+              <el-button
+                v-if="!canEditBaidu"
+                type="primary"
+                @click="handleEditBaidu"
+              >
+                <i class="el-icon-edit"></i> 缂栬緫閰嶇疆
+              </el-button>
+              <el-button
+                v-if="canEditBaidu"
+                type="success"
+                @click="handleSaveBaidu"
+              >
+                <i class="el-icon-check"></i> 淇濆瓨閰嶇疆
+              </el-button>
+              <el-button
+                v-if="canEditBaidu"
+                @click="handleCancelBaidu"
+              >
+                <i class="el-icon-close"></i> 鍙栨秷
+              </el-button>
+            </el-form-item>
+          </el-form>
+        </el-tab-pane>
+      </el-tabs>
+    </el-card>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "OCRConfig",
+  data() {
+    return {
+      activeTab: 'ali',
+      // 闃块噷浜慜CR閰嶇疆
+      canEditAli: false,
+      aliForm: {
+        accessKeyId: '',
+        accessKeySecret: ''
+      },
+      aliOcrEndpoint: 'ocr-api.cn-hangzhou.aliyuncs.com',
+      // 鐧惧害OCR閰嶇疆
+      canEditBaidu: false,
+      baiduForm: {
+        appId: '',
+        apiKey: '',
+        secretKey: ''
+      },
+      rules: {
+        accessKeyId: [
+          { required: true, message: '璇疯緭鍏ccessKey ID', trigger: 'blur' },
+          { min: 10, max: 64, message: '闀垮害鍦�10鍒�64涓瓧绗�', trigger: 'blur' }
+        ],
+        accessKeySecret: [
+          { required: true, message: '璇疯緭鍏ccessKey Secret', trigger: 'blur' },
+          { min: 10, max: 64, message: '闀垮害鍦�10鍒�64涓瓧绗�', trigger: 'blur' }
+        ]
+      },
+      baiduRules: {
+        appId: [
+          { required: true, message: '璇疯緭鍏pp ID', trigger: 'blur' },
+          { min: 5, max: 32, message: '闀垮害鍦�5鍒�32涓瓧绗�', trigger: 'blur' }
+        ],
+        apiKey: [
+          { required: true, message: '璇疯緭鍏PI Key', trigger: 'blur' },
+          { min: 10, max: 64, message: '闀垮害鍦�10鍒�64涓瓧绗�', trigger: 'blur' }
+        ],
+        secretKey: [
+          { required: true, message: '璇疯緭鍏ecret Key', trigger: 'blur' },
+          { min: 10, max: 64, message: '闀垮害鍦�10鍒�64涓瓧绗�', trigger: 'blur' }
+        ]
+      }
+    };
+  },
+  methods: {
+    /** 缂栬緫闃块噷浜慜CR閰嶇疆 */
+    handleEditAli() {
+      this.canEditAli = true;
+    },
+
+    /** 淇濆瓨闃块噷浜慜CR閰嶇疆 */
+    handleSaveAli() {
+      this.$refs.aliForm.validate(valid => {
+        if (valid) {
+          this.$confirm('淇濆瓨闃块噷浜慜CR閰嶇疆鍚庨渶瑕侀噸鍚簲鐢ㄦ墠鑳界敓鏁堬紝鏄惁缁х画锛�', '鎻愮ず', {
+            confirmButtonText: '纭畾',
+            cancelButtonText: '鍙栨秷',
+            type: 'warning'
+          }).then(() => {
+            // 杩欓噷搴旇璋冪敤鍚庣API淇濆瓨閰嶇疆
+            this.$modal.msgSuccess('闃块噷浜慜CR閰嶇疆淇濆瓨鎴愬姛锛岃閲嶅惎搴旂敤浣块厤缃敓鏁�');
+            this.canEditAli = false;
+          });
+        }
+      });
+    },
+
+    /** 鍙栨秷闃块噷浜慜CR缂栬緫 */
+    handleCancelAli() {
+      this.canEditAli = false;
+      // 鎭㈠鍘熷鍊硷紙杩欓噷搴旇浠庡悗绔幏鍙栵級
+      this.aliForm = {
+        accessKeyId: '',
+        accessKeySecret: ''
+      };
+    },
+
+    /** 缂栬緫鐧惧害OCR閰嶇疆 */
+    handleEditBaidu() {
+      this.canEditBaidu = true;
+    },
+
+    /** 淇濆瓨鐧惧害OCR閰嶇疆 */
+    handleSaveBaidu() {
+      this.$refs.baiduForm.validate(valid => {
+        if (valid) {
+          this.$confirm('淇濆瓨鐧惧害OCR閰嶇疆鍚庨渶瑕侀噸鍚簲鐢ㄦ墠鑳界敓鏁堬紝鏄惁缁х画锛�', '鎻愮ず', {
+            confirmButtonText: '纭畾',
+            cancelButtonText: '鍙栨秷',
+            type: 'warning'
+          }).then(() => {
+            // 杩欓噷搴旇璋冪敤鍚庣API淇濆瓨閰嶇疆
+            this.$modal.msgSuccess('鐧惧害OCR閰嶇疆淇濆瓨鎴愬姛锛岃閲嶅惎搴旂敤浣块厤缃敓鏁�');
+            this.canEditBaidu = false;
+          });
+        }
+      });
+    },
+
+    /** 鍙栨秷鐧惧害OCR缂栬緫 */
+    handleCancelBaidu() {
+      this.canEditBaidu = false;
+      // 鎭㈠鍘熷鍊硷紙杩欓噷搴旇浠庡悗绔幏鍙栵級
+      this.baiduForm = {
+        appId: '',
+        apiKey: '',
+        secretKey: ''
+      };
+    }
+  }
+};
+</script>
+
+<style scoped>
+.box-card {
+  margin-bottom: 20px;
+}
+</style>
\ No newline at end of file
diff --git a/ruoyi-ui/src/views/system/ocr/index.vue b/ruoyi-ui/src/views/system/ocr/index.vue
new file mode 100644
index 0000000..5a590ef
--- /dev/null
+++ b/ruoyi-ui/src/views/system/ocr/index.vue
@@ -0,0 +1,386 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-card">
+      <div slot="header" class="clearfix">
+        <span>OCR鍥惧儚璇嗗埆娴嬭瘯</span>
+      </div>
+
+      <!-- 鍔熻兘璇存槑 -->
+      <el-alert
+        title="鍔熻兘璇存槑"
+        type="info"
+        :closable="false"
+        style="margin-bottom: 20px"
+      >
+        <div>
+          鏈〉闈㈢敤浜庢祴璇曢樋閲屼簯OCR銆佺櫨搴CR鍜岃吘璁簯OCR鍥惧儚璇嗗埆鍔熻兘銆傛敮鎸侀�氱敤鏂囧瓧璇嗗埆銆佸彂绁ㄨ瘑鍒�佽韩浠借瘉璇嗗埆銆佹墜鍐欎綋璇嗗埆绛夈��<br/>
+          <strong>鏀寔鏍煎紡锛�</strong>JPG銆丳NG銆丅MP绛夊父瑙佸浘鐗囨牸寮忥紱<strong>鏂囦欢澶у皬锛�</strong>涓嶈秴杩�4MB
+        </div>
+      </el-alert>
+
+      <!-- 璇嗗埆绫诲瀷閫夋嫨 -->
+      <el-form :inline="true" style="margin-bottom: 20px">
+        <el-form-item label="璇嗗埆绫诲瀷">
+          <el-select v-model="recognizeType" placeholder="璇烽�夋嫨璇嗗埆绫诲瀷" style="width: 200px">
+            <el-option label="閫氱敤鏂囧瓧璇嗗埆" value="General" />
+            <el-option label="鍙戠エ璇嗗埆" value="Invoice" />
+            <el-option label="韬唤璇佽瘑鍒�" value="IdCard" />
+            <el-option label="鎵嬪啓浣撹瘑鍒�" value="HandWriting" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="OCR鏈嶅姟">
+          <el-select v-model="provider" placeholder="璇烽�夋嫨OCR鏈嶅姟鎻愪緵鍟�" style="width: 200px">
+            <el-option label="闃块噷浜慜CR" value="ali" />
+            <el-option label="鐧惧害OCR" value="baidu" />
+            <el-option label="鑵捐浜慜CR" value="tencent" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鎻愬彇瀛楁">
+          <el-input
+            v-if="provider === 'tencent' && recognizeType === 'HandWriting'"
+            v-model="itemNames"
+            placeholder="璇疯緭鍏ラ渶瑕佹彁鍙栫殑瀛楁锛岀敤閫楀彿鍒嗛殧"
+            style="width: 200px"
+            clearable
+          />
+        </el-form-item>
+      </el-form>
+
+      <!-- 鍥剧墖涓婁紶鍖哄煙 -->
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-card shadow="hover">
+            <div slot="header">
+              <span>鍥剧墖涓婁紶</span>
+            </div>
+            
+            <el-upload
+              ref="upload"
+              class="upload-demo"
+              drag
+              action="#"
+              :auto-upload="false"
+              :on-change="handleFileChange"
+              :limit="1"
+              :file-list="fileList"
+              accept="image/*"
+            >
+              <i class="el-icon-upload"></i>
+              <div class="el-upload__text">灏嗗浘鐗囨嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
+              <div class="el-upload__tip" slot="tip">
+                鏀寔JPG銆丳NG銆丅MP鏍煎紡锛屾枃浠朵笉瓒呰繃4MB
+              </div>
+            </el-upload>
+
+            <div style="margin-top: 20px; text-align: center">
+              <el-button
+                type="primary"
+                :loading="recognizing"
+                :disabled="!fileList.length"
+                @click="handleRecognize"
+              >
+                <i class="el-icon-view"></i> 寮�濮嬭瘑鍒�
+              </el-button>
+              <el-button @click="handleClear">
+                <i class="el-icon-delete"></i> 娓呯┖
+              </el-button>
+            </div>
+
+            <!-- 鍥剧墖棰勮 -->
+            <div v-if="imagePreview" style="margin-top: 20px">
+              <el-divider>鍥剧墖棰勮</el-divider>
+              <div style="text-align: center">
+                <el-image
+                  :src="imagePreview"
+                  fit="contain"
+                  style="max-width: 100%; max-height: 400px"
+                  :preview-src-list="[imagePreview]"
+                />
+              </div>
+            </div>
+          </el-card>
+        </el-col>
+
+        <!-- 璇嗗埆缁撴灉鍖哄煙 -->
+        <el-col :span="12">
+          <el-card shadow="hover" style="min-height: 500px">
+            <div slot="header">
+              <span>璇嗗埆缁撴灉</span>
+              <el-button
+                v-if="ocrResult"
+                style="float: right; padding: 3px 0"
+                type="text"
+                @click="handleCopyResult"
+              >
+                <i class="el-icon-document-copy"></i> 澶嶅埗缁撴灉
+              </el-button>
+            </div>
+
+            <!-- 鍔犺浇涓� -->
+            <div v-if="recognizing" style="text-align: center; padding: 50px 0">
+              <i class="el-icon-loading" style="font-size: 40px; color: #409EFF"></i>
+              <p style="margin-top: 20px; color: #909399">姝e湪璇嗗埆涓紝璇风◢鍊�...({{ provider === 'ali' ? '闃块噷浜�' : provider === 'baidu' ? '鐧惧害' : '鑵捐浜�' }})</p>
+            </div>
+
+            <!-- 璇嗗埆鎴愬姛 -->
+            <div v-else-if="ocrResult && ocrResult.success">
+              <el-alert
+                title="璇嗗埆鎴愬姛"
+                type="success"
+                :closable="false"
+                style="margin-bottom: 15px"
+              >
+                <div>鏈嶅姟鎻愪緵鍟�: {{ provider === 'ali' ? '闃块噷浜慜CR' : provider === 'baidu' ? '鐧惧害OCR' : '鑵捐浜慜CR' }}</div>
+              </el-alert>
+
+              <!-- 鎻愬彇鐨勫瓧娈典俊鎭� -->
+              <div v-if="extractedFields && Object.keys(extractedFields).length > 0">
+                <el-descriptions title="鎻愬彇瀛楁" :column="1" border>
+                  <el-descriptions-item
+                    v-for="(value, key) in extractedFields"
+                    :key="key"
+                    :label="getFieldLabel(key)"
+                  >
+                    {{ value }}
+                  </el-descriptions-item>
+                </el-descriptions>
+                <el-divider />
+              </div>
+
+              <!-- 瀹屾暣璇嗗埆鍐呭 -->
+              <div>
+                <h4>瀹屾暣璇嗗埆鍐呭锛�</h4>
+                <el-input
+                  type="textarea"
+                  :value="getFullContent(ocrResult)"
+                  :autosize="{ minRows: 10, maxRows: 20 }"
+                  readonly
+                  style="margin-top: 10px"
+                />
+              </div>
+
+              <!-- 鍘熷JSON鏁版嵁 -->
+              <el-collapse style="margin-top: 20px">
+                <el-collapse-item title="鏌ョ湅鍘熷JSON鏁版嵁" name="json">
+                  <pre style="background: #f5f7fa; padding: 15px; border-radius: 4px; max-height: 400px; overflow: auto">{{ JSON.stringify(ocrResult, null, 2) }}</pre>
+                </el-collapse-item>
+              </el-collapse>
+            </div>
+
+            <!-- 璇嗗埆澶辫触 -->
+            <div v-else-if="ocrResult && !ocrResult.success">
+              <el-alert
+                title="璇嗗埆澶辫触"
+                :description="ocrResult.error || '鏈煡閿欒'"
+                type="error"
+                :closable="false"
+              >
+                <div>鏈嶅姟鎻愪緵鍟�: {{ provider === 'ali' ? '闃块噷浜慜CR' : provider === 'baidu' ? '鐧惧害OCR' : '鑵捐浜慜CR' }}</div>
+              </el-alert>
+              <div v-if="ocrResult.detail" style="margin-top: 15px">
+                <el-tag type="warning">{{ ocrResult.detail }}</el-tag>
+              </div>
+            </div>
+
+            <!-- 鏈紑濮嬭瘑鍒� -->
+            <div v-else style="text-align: center; padding: 50px 0; color: #909399">
+              <i class="el-icon-picture-outline" style="font-size: 60px"></i>
+              <p style="margin-top: 20px">璇蜂笂浼犲浘鐗囧苟鐐瑰嚮"寮�濮嬭瘑鍒�"鎸夐挳</p>
+            </div>
+          </el-card>
+        </el-col>
+      </el-row>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import { recognizeImage, extractFields } from "@/api/system/ocr";
+
+export default {
+  name: "OCRTest",
+  data() {
+    return {
+      // 璇嗗埆绫诲瀷
+      recognizeType: "General",
+      // OCR鏈嶅姟鎻愪緵鍟�
+      provider: "ali",
+      // 鑵捐浜慜CR鎻愬彇瀛楁
+      itemNames: "鎮h�呭鍚�,鎬у埆,骞撮緞,韬唤璇佸彿,璇婃柇,闇�鏀粯杞繍璐圭敤,琛岀▼,寮�濮嬫椂闂�,缁撴潫鏃堕棿,瀹跺睘绛惧悕",
+      // 鏂囦欢鍒楄〃
+      fileList: [],
+      // 褰撳墠鏂囦欢
+      currentFile: null,
+      // 鍥剧墖棰勮
+      imagePreview: null,
+      // 璇嗗埆涓�
+      recognizing: false,
+      // OCR璇嗗埆缁撴灉
+      ocrResult: null,
+      // 鎻愬彇鐨勫瓧娈�
+      extractedFields: null
+    };
+  },
+  methods: {
+    /** 鏂囦欢閫夋嫨鍙樺寲 */
+    handleFileChange(file, fileList) {
+      // 闄愬埗鍙兘涓婁紶涓�涓枃浠�
+      if (fileList.length > 1) {
+        fileList.splice(0, 1);
+      }
+      
+      this.fileList = fileList;
+      this.currentFile = file.raw;
+      
+      // 鐢熸垚鍥剧墖棰勮
+      const reader = new FileReader();
+      reader.onload = (e) => {
+        this.imagePreview = e.target.result;
+      };
+      reader.readAsDataURL(file.raw);
+      
+      // 娓呯┖涔嬪墠鐨勮瘑鍒粨鏋�
+      this.ocrResult = null;
+      this.extractedFields = null;
+    },
+
+    /** 寮�濮嬭瘑鍒� */
+    handleRecognize() {
+      if (!this.currentFile) {
+        this.$modal.msgWarning("璇峰厛涓婁紶鍥剧墖");
+        return;
+      }
+
+      // 妫�鏌ユ枃浠跺ぇ灏忥紙4MB锛�
+      const maxSize = 4 * 1024 * 1024;
+      if (this.currentFile.size > maxSize) {
+        this.$modal.msgError("鍥剧墖澶у皬涓嶈兘瓒呰繃4MB");
+        return;
+      }
+
+      this.recognizing = true;
+      this.ocrResult = null;
+      this.extractedFields = null;
+
+      // 鏋勫缓FormData
+      const formData = new FormData();
+      formData.append("file", this.currentFile);
+      formData.append("type", this.recognizeType);
+      formData.append("provider", this.provider);
+      
+      // 濡傛灉鏄吘璁簯OCR鎵嬪啓浣撹瘑鍒紝鍒欐坊鍔爄temNames鍙傛暟
+      if (this.provider === 'tencent' && this.recognizeType === 'HandWriting' && this.itemNames) {
+        const itemNamesArray = this.itemNames.split(',').map(item => item.trim()).filter(item => item);
+        itemNamesArray.forEach((itemName, index) => {
+          formData.append(`itemNames[${index}]`, itemName);
+        });
+      }
+
+      // 璋冪敤OCR璇嗗埆鎺ュ彛
+      recognizeImage(formData).then(response => {
+        this.ocrResult = response.data.ocrResult;
+        
+        // 鑷姩鎻愬彇瀛楁
+        if (this.ocrResult.success) {
+          extractFields(this.ocrResult).then(res => {
+            this.extractedFields = res.data;
+          }).catch(() => {
+            // 鎻愬彇澶辫触涓嶅奖鍝嶄富娴佺▼
+          });
+        }
+        
+        this.recognizing = false;
+      }).catch(error => {
+        this.$modal.msgError("OCR璇嗗埆澶辫触: " + (error.message || "鏈煡閿欒"));
+        this.recognizing = false;
+      });
+    },
+
+    /** 娓呯┖ */
+    handleClear() {
+      this.fileList = [];
+      this.currentFile = null;
+      this.imagePreview = null;
+      this.ocrResult = null;
+      this.extractedFields = null;
+      this.$refs.upload.clearFiles();
+    },
+
+    /** 澶嶅埗璇嗗埆缁撴灉 */
+    handleCopyResult() {
+      const content = this.getFullContent(this.ocrResult);
+      this.copyToClipboard(content);
+      this.$modal.msgSuccess("澶嶅埗鎴愬姛");
+    },
+
+    /** 澶嶅埗鍒板壀璐存澘 */
+    copyToClipboard(text) {
+      const textarea = document.createElement("textarea");
+      textarea.value = text;
+      document.body.appendChild(textarea);
+      textarea.select();
+      document.execCommand("copy");
+      document.body.removeChild(textarea);
+    },
+
+    /** 鑾峰彇瀹屾暣璇嗗埆鍐呭 */
+    getFullContent(ocrResult) {
+      if (!ocrResult) return "";
+      
+      if (ocrResult.content) {
+        return ocrResult.content;
+      }
+      
+      // 濡傛灉鏈塸rism_wordsInfo锛屾嫾鎺ユ墍鏈夋枃瀛�
+      if (ocrResult.prism_wordsInfo && Array.isArray(ocrResult.prism_wordsInfo)) {
+        return ocrResult.prism_wordsInfo.map(item => item.word).join("\n");
+      }
+      
+      return JSON.stringify(ocrResult, null, 2);
+    },
+
+    /** 鑾峰彇瀛楁鏍囩 */
+    getFieldLabel(key) {
+      const labelMap = {
+        totalAmount: "鎬婚噾棰�",
+        date: "鏃ユ湡",
+        remark: "澶囨敞",
+        fullText: "鍏ㄦ枃",
+        error: "閿欒淇℃伅"
+      };
+      return labelMap[key] || key;
+    }
+  }
+};
+</script>
+
+<style scoped>
+.box-card {
+  margin-bottom: 20px;
+}
+
+.upload-demo {
+  width: 100%;
+}
+
+.el-upload-dragger {
+  width: 100%;
+}
+
+pre {
+  font-family: 'Courier New', Courier, monospace;
+  font-size: 12px;
+  line-height: 1.5;
+  white-space: pre-wrap;
+  word-wrap: break-word;
+}
+
+.el-descriptions {
+  margin-top: 10px;
+}
+
+h4 {
+  margin: 15px 0 10px 0;
+  color: #303133;
+}
+</style>
\ No newline at end of file
diff --git a/ruoyi-ui/src/views/system/vehicleAlert/index.vue b/ruoyi-ui/src/views/system/vehicleAlert/index.vue
new file mode 100644
index 0000000..e4024c2
--- /dev/null
+++ b/ruoyi-ui/src/views/system/vehicleAlert/index.vue
@@ -0,0 +1,528 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="88px">
+      <el-form-item label="杞︾墝鍙�" prop="vehicleNo">
+        <el-input
+          v-model="queryParams.vehicleNo"
+          placeholder="璇疯緭鍏ヨ溅鐗屽彿"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="鍛婅鏃ユ湡" prop="alertDate">
+        <el-date-picker
+          v-model="queryParams.alertDate"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="璇烽�夋嫨鍛婅鏃ユ湡"
+          clearable
+          size="small"
+        />
+      </el-form-item>
+      <el-form-item label="鍛婅鐘舵��" prop="status">
+        <el-select v-model="queryParams.status" placeholder="璇烽�夋嫨鐘舵��" clearable size="small">
+          <el-option label="鏈鐞�" value="0" />
+          <el-option label="宸插鐞�" value="1" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="褰掑睘閮ㄩ棬" prop="deptId">
+        <el-select v-model="queryParams.deptId" placeholder="璇烽�夋嫨閮ㄩ棬" clearable size="small">
+          <el-option
+            v-for="dept in deptList"
+            :key="dept.deptId"
+            :label="dept.deptName"
+            :value="dept.deptId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鍛婅鏃堕棿">
+        <el-date-picker
+          v-model="dateRange"
+          size="small"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="寮�濮嬫棩鏈�"
+          end-placeholder="缁撴潫鏃ユ湡"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">鎼滅储</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-check"
+          size="mini"
+          :disabled="multiple"
+          @click="handleBatchProcess"
+          v-hasPermi="['system:vehicleAlert:handle']"
+        >鎵归噺澶勭悊</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['system:vehicleAlert:remove']"
+        >鍒犻櫎</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['system:vehicleAlert:export']"
+        >瀵煎嚭</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="info"
+          plain
+          icon="el-icon-refresh"
+          size="mini"
+          @click="refreshUnhandledCount"
+        >鍒锋柊缁熻</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <!-- 缁熻鍗$墖 -->
+    <el-row :gutter="20" class="mb8">
+      <el-col :span="6">
+        <el-card shadow="hover">
+          <div style="text-align: center;">
+            <div style="font-size: 14px; color: #909399;">鏈鐞嗗憡璀�</div>
+            <div style="font-size: 28px; color: #F56C6C; font-weight: bold; margin-top: 10px;">
+              {{ unhandledCount }}
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="6">
+        <el-card shadow="hover">
+          <div style="text-align: center;">
+            <div style="font-size: 14px; color: #909399;">浠婃棩鍛婅</div>
+            <div style="font-size: 28px; color: #E6A23C; font-weight: bold; margin-top: 10px;">
+              {{ todayCount }}
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="6">
+        <el-card shadow="hover">
+          <div style="text-align: center;">
+            <div style="font-size: 14px; color: #909399;">绱鍛婅杞﹁締</div>
+            <div style="font-size: 28px; color: #409EFF; font-weight: bold; margin-top: 10px;">
+              {{ totalVehicles }}
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="6">
+        <el-card shadow="hover">
+          <div style="text-align: center;">
+            <div style="font-size: 14px; color: #909399;">绱鍛婅娆℃暟</div>
+            <div style="font-size: 28px; color: #67C23A; font-weight: bold; margin-top: 10px;">
+              {{ total }}
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <el-table v-loading="loading" :data="alertList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="鍛婅ID" align="center" prop="alertId" width="80" />
+      <el-table-column label="杞︾墝鍙�" align="center" prop="vehicleNo" width="120">
+        <template slot-scope="scope">
+          <el-tag>{{ scope.row.vehicleNo }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="鍛婅鏃ユ湡" align="center" prop="alertDate" width="120" />
+      <el-table-column label="鍛婅鏃堕棿" align="center" prop="alertTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.alertTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="杩愯閲岀▼(鍏噷)" align="center" prop="mileage" width="140">
+        <template slot-scope="scope">
+          <el-tag type="danger" v-if="scope.row.mileage >= 10">
+            {{ scope.row.mileage }} km
+          </el-tag>
+          <span v-else>{{ scope.row.mileage }} km</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="褰撴棩鍛婅娆℃暟" align="center" prop="alertCount" width="120">
+        <template slot-scope="scope">
+          <el-tag type="warning" v-if="scope.row.alertCount >= 3">
+            绗瑊{ scope.row.alertCount }}娆�
+          </el-tag>
+          <span v-else>绗瑊{ scope.row.alertCount }}娆�</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="褰掑睘閮ㄩ棬" align="center" prop="deptName" width="150" :show-overflow-tooltip="true" />
+      <el-table-column label="鍛婅鐘舵��" align="center" prop="status" width="100">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.status === '0'" type="danger">鏈鐞�</el-tag>
+          <el-tag v-else type="success">宸插鐞�</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="閫氱煡鐘舵��" align="center" prop="notifyStatus" width="100">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.notifyStatus === '0'" type="info">鏈彂閫�</el-tag>
+          <el-tag v-else-if="scope.row.notifyStatus === '1'" type="success">宸插彂閫�</el-tag>
+          <el-tag v-else type="danger">鍙戦�佸け璐�</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="澶勭悊浜�" align="center" prop="handlerName" width="100" :show-overflow-tooltip="true" />
+      <el-table-column label="澶勭悊鏃堕棿" align="center" prop="handleTime" width="180">
+        <template slot-scope="scope">
+          <span v-if="scope.row.handleTime">{{ parseTime(scope.row.handleTime) }}</span>
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleView(scope.row)"
+            v-hasPermi="['system:vehicleAlert:query']"
+          >璇︽儏</el-button>
+          <el-button
+            v-if="scope.row.status === '0'"
+            size="mini"
+            type="text"
+            icon="el-icon-check"
+            @click="handleProcess(scope.row)"
+            v-hasPermi="['system:vehicleAlert:handle']"
+          >澶勭悊</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['system:vehicleAlert:remove']"
+          >鍒犻櫎</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 鍛婅璇︽儏瀵硅瘽妗� -->
+    <el-dialog title="鍛婅璇︽儏" :visible.sync="detailOpen" width="700px" append-to-body>
+      <el-descriptions :column="2" border>
+        <el-descriptions-item label="鍛婅ID">{{ detail.alertId }}</el-descriptions-item>
+        <el-descriptions-item label="杞︾墝鍙�">
+          <el-tag>{{ detail.vehicleNo }}</el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="鍛婅鏃ユ湡">{{ detail.alertDate }}</el-descriptions-item>
+        <el-descriptions-item label="鍛婅鏃堕棿">{{ parseTime(detail.alertTime) }}</el-descriptions-item>
+        <el-descriptions-item label="杩愯閲岀▼">
+          <span style="color: #F56C6C; font-weight: bold;">{{ detail.mileage }} 鍏噷</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="褰撴棩鍛婅娆℃暟">绗瑊{ detail.alertCount }}娆�</el-descriptions-item>
+        <el-descriptions-item label="褰掑睘閮ㄩ棬" :span="2">{{ detail.deptName }}</el-descriptions-item>
+        <el-descriptions-item label="鍛婅鐘舵��">
+          <el-tag v-if="detail.status === '0'" type="danger">鏈鐞�</el-tag>
+          <el-tag v-else type="success">宸插鐞�</el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="閫氱煡鐘舵��">
+          <el-tag v-if="detail.notifyStatus === '0'" type="info">鏈彂閫�</el-tag>
+          <el-tag v-else-if="detail.notifyStatus === '1'" type="success">宸插彂閫�</el-tag>
+          <el-tag v-else type="danger">鍙戦�佸け璐�</el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="閫氱煡鐢ㄦ埛" :span="2">{{ detail.notifyUsers || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="閫氱煡娑堟伅" :span="2">{{ detail.notifyMessage || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="澶勭悊浜�">{{ detail.handlerName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="澶勭悊鏃堕棿">{{ parseTime(detail.handleTime) || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="澶勭悊澶囨敞" :span="2">{{ detail.handleRemark || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="鍒涘缓鏃堕棿" :span="2">{{ parseTime(detail.createTime) }}</el-descriptions-item>
+      </el-descriptions>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="detailOpen = false">鍏� 闂�</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 澶勭悊鍛婅瀵硅瘽妗� -->
+    <el-dialog title="澶勭悊鍛婅" :visible.sync="processOpen" width="500px" append-to-body>
+      <el-form ref="processForm" :model="processForm" :rules="processRules" label-width="100px">
+        <el-form-item label="澶勭悊澶囨敞" prop="handleRemark">
+          <el-input
+            v-model="processForm.handleRemark"
+            type="textarea"
+            :rows="4"
+            placeholder="璇疯緭鍏ュ鐞嗗娉紙蹇呭~锛�"
+          />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitProcess">纭� 瀹�</el-button>
+        <el-button @click="processOpen = false">鍙� 娑�</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 鎵归噺澶勭悊瀵硅瘽妗� -->
+    <el-dialog title="鎵归噺澶勭悊鍛婅" :visible.sync="batchProcessOpen" width="500px" append-to-body>
+      <el-alert
+        :title="`宸查�夋嫨 ${ids.length} 鏉℃湭澶勭悊鍛婅璁板綍`"
+        type="info"
+        :closable="false"
+        style="margin-bottom: 20px;"
+      />
+      <el-form ref="batchProcessForm" :model="batchProcessForm" :rules="batchProcessRules" label-width="100px">
+        <el-form-item label="澶勭悊澶囨敞" prop="handleRemark">
+          <el-input
+            v-model="batchProcessForm.handleRemark"
+            type="textarea"
+            :rows="4"
+            placeholder="璇疯緭鍏ュ鐞嗗娉紙蹇呭~锛�"
+          />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitBatchProcess">纭� 瀹�</el-button>
+        <el-button @click="batchProcessOpen = false">鍙� 娑�</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listVehicleAlert, getVehicleAlert, delVehicleAlert, handleAlert, batchHandleAlert, getUnhandledCount, exportVehicleAlert } from "@/api/system/vehicleAlert";
+import { listDept } from "@/api/system/dept";
+
+export default {
+  name: "VehicleAlert",
+  data() {
+    return {
+      // 閬僵灞�
+      loading: true,
+      // 閫変腑鏁扮粍
+      ids: [],
+      // 闈炲崟涓鐢�
+      single: true,
+      // 闈炲涓鐢�
+      multiple: true,
+      // 鏄剧ず鎼滅储鏉′欢
+      showSearch: true,
+      // 鎬绘潯鏁�
+      total: 0,
+      // 杞﹁締寮傚父鍛婅琛ㄦ牸鏁版嵁
+      alertList: [],
+      // 閮ㄩ棬鍒楄〃
+      deptList: [],
+      // 寮瑰嚭灞傛爣棰�
+      title: "",
+      // 鏄惁鏄剧ず璇︽儏寮瑰嚭灞�
+      detailOpen: false,
+      // 鏄惁鏄剧ず澶勭悊寮瑰嚭灞�
+      processOpen: false,
+      // 鏄惁鏄剧ず鎵归噺澶勭悊寮瑰嚭灞�
+      batchProcessOpen: false,
+      // 鏃ユ湡鑼冨洿
+      dateRange: [],
+      // 璇︽儏鏁版嵁
+      detail: {},
+      // 澶勭悊琛ㄥ崟
+      processForm: {
+        alertId: null,
+        handleRemark: ''
+      },
+      // 鎵归噺澶勭悊琛ㄥ崟
+      batchProcessForm: {
+        handleRemark: ''
+      },
+      // 缁熻鏁版嵁
+      unhandledCount: 0,
+      todayCount: 0,
+      totalVehicles: 0,
+      // 鏌ヨ鍙傛暟
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        vehicleNo: null,
+        alertDate: null,
+        status: null,
+        deptId: null
+      },
+      // 澶勭悊琛ㄥ崟鏍¢獙
+      processRules: {
+        handleRemark: [
+          { required: true, message: "澶勭悊澶囨敞涓嶈兘涓虹┖", trigger: "blur" }
+        ]
+      },
+      // 鎵归噺澶勭悊琛ㄥ崟鏍¢獙
+      batchProcessRules: {
+        handleRemark: [
+          { required: true, message: "澶勭悊澶囨敞涓嶈兘涓虹┖", trigger: "blur" }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+    this.getDeptList();
+    this.refreshUnhandledCount();
+  },
+  methods: {
+    /** 鏌ヨ杞﹁締寮傚父鍛婅鍒楄〃 */
+    getList() {
+      this.loading = true;
+      listVehicleAlert(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+        this.alertList = response.rows;
+        this.total = response.total;
+        // 缁熻浠婃棩鍛婅鏁伴噺
+        const today = this.parseTime(new Date(), '{y}-{m}-{d}');
+        this.todayCount = this.alertList.filter(item => item.alertDate === today).length;
+        // 缁熻鍛婅杞﹁締鏁伴噺锛堝幓閲嶏級
+        const vehicleSet = new Set(this.alertList.map(item => item.vehicleId));
+        this.totalVehicles = vehicleSet.size;
+        this.loading = false;
+      });
+    },
+    /** 鏌ヨ閮ㄩ棬鍒楄〃 */
+    getDeptList() {
+      listDept().then(response => {
+        this.deptList = response.data;
+      });
+    },
+    /** 鍒锋柊鏈鐞嗗憡璀︾粺璁� */
+    refreshUnhandledCount() {
+      getUnhandledCount().then(response => {
+        this.unhandledCount = response.data;
+      });
+    },
+    /** 鎼滅储鎸夐挳鎿嶄綔 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 閲嶇疆鎸夐挳鎿嶄綔 */
+    resetQuery() {
+      this.dateRange = [];
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 澶氶�夋閫変腑鏁版嵁
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.alertId);
+      this.single = selection.length !== 1;
+      this.multiple = !selection.length;
+    },
+    /** 鏌ョ湅璇︽儏鎸夐挳鎿嶄綔 */
+    handleView(row) {
+      getVehicleAlert(row.alertId).then(response => {
+        this.detail = response.data;
+        this.detailOpen = true;
+      });
+    },
+    /** 澶勭悊鎸夐挳鎿嶄綔 */
+    handleProcess(row) {
+      this.processForm = {
+        alertId: row.alertId,
+        handleRemark: ''
+      };
+      this.processOpen = true;
+      this.$nextTick(() => {
+        this.$refs["processForm"].clearValidate();
+      });
+    },
+    /** 鎻愪氦澶勭悊 */
+    submitProcess() {
+      this.$refs["processForm"].validate(valid => {
+        if (valid) {
+          handleAlert(this.processForm.alertId, {
+            handleRemark: this.processForm.handleRemark
+          }).then(response => {
+            this.$modal.msgSuccess("澶勭悊鎴愬姛");
+            this.processOpen = false;
+            this.getList();
+            this.refreshUnhandledCount();
+          });
+        }
+      });
+    },
+    /** 鎵归噺澶勭悊鎸夐挳鎿嶄綔 */
+    handleBatchProcess() {
+      // 绛涢�夊嚭鏈鐞嗙殑璁板綍
+      const unhandledIds = this.ids.filter(id => {
+        const alert = this.alertList.find(item => item.alertId === id);
+        return alert && alert.status === '0';
+      });
+      
+      if (unhandledIds.length === 0) {
+        this.$modal.msgWarning("璇烽�夋嫨鏈鐞嗙殑鍛婅璁板綍");
+        return;
+      }
+      
+      this.ids = unhandledIds;
+      this.batchProcessForm = {
+        handleRemark: ''
+      };
+      this.batchProcessOpen = true;
+      this.$nextTick(() => {
+        this.$refs["batchProcessForm"].clearValidate();
+      });
+    },
+    /** 鎻愪氦鎵归噺澶勭悊 */
+    submitBatchProcess() {
+      this.$refs["batchProcessForm"].validate(valid => {
+        if (valid) {
+          batchHandleAlert(this.ids, {
+            handleRemark: this.batchProcessForm.handleRemark
+          }).then(response => {
+            this.$modal.msgSuccess("鎵归噺澶勭悊鎴愬姛");
+            this.batchProcessOpen = false;
+            this.getList();
+            this.refreshUnhandledCount();
+          });
+        }
+      });
+    },
+    /** 鍒犻櫎鎸夐挳鎿嶄綔 */
+    handleDelete(row) {
+      const alertIds = row.alertId || this.ids;
+      this.$modal.confirm('鏄惁纭鍒犻櫎鍛婅璁板綍缂栧彿涓�"' + alertIds + '"鐨勬暟鎹」锛�').then(function() {
+        return delVehicleAlert(alertIds);
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+      }).catch(() => {});
+    },
+    /** 瀵煎嚭鎸夐挳鎿嶄綔 */
+    handleExport() {
+      this.download('system/vehicleAlert/export', {
+        ...this.queryParams
+      }, `杞﹁締寮傚父鍛婅_${new Date().getTime()}.xlsx`)
+    }
+  }
+};
+</script>
+
+<style scoped>
+.mb8 {
+  margin-bottom: 8px;
+}
+</style>
diff --git a/ruoyi-ui/src/views/system/vehicleAlertConfig/index.vue b/ruoyi-ui/src/views/system/vehicleAlertConfig/index.vue
new file mode 100644
index 0000000..ebc0eee
--- /dev/null
+++ b/ruoyi-ui/src/views/system/vehicleAlertConfig/index.vue
@@ -0,0 +1,486 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="88px">
+      <el-form-item label="閰嶇疆绫诲瀷" prop="configType">
+        <el-select v-model="queryParams.configType" placeholder="璇烽�夋嫨閰嶇疆绫诲瀷" clearable size="small">
+          <el-option label="鍏ㄥ眬閰嶇疆" value="GLOBAL" />
+          <el-option label="閮ㄩ棬閰嶇疆" value="DEPT" />
+          <el-option label="杞﹁締閰嶇疆" value="VEHICLE" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="閮ㄩ棬" prop="deptId" v-if="queryParams.configType === 'DEPT'">
+        <el-select v-model="queryParams.deptId" placeholder="璇烽�夋嫨閮ㄩ棬" clearable size="small">
+          <el-option
+            v-for="dept in deptList"
+            :key="dept.deptId"
+            :label="dept.deptName"
+            :value="dept.deptId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="杞﹁締" prop="vehicleId" v-if="queryParams.configType === 'VEHICLE'">
+        <el-select v-model="queryParams.vehicleId" placeholder="璇烽�夋嫨杞﹁締" clearable size="small" filterable>
+          <el-option
+            v-for="vehicle in vehicleList"
+            :key="vehicle.vehicleId"
+            :label="vehicle.vehicleNo"
+            :value="vehicle.vehicleId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鐘舵��" prop="status">
+        <el-select v-model="queryParams.status" placeholder="璇烽�夋嫨鐘舵��" clearable size="small">
+          <el-option label="鍚敤" value="0" />
+          <el-option label="鍋滅敤" value="1" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">鎼滅储</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['system:vehicleAlertConfig:add']"
+        >鏂板</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['system:vehicleAlertConfig:edit']"
+        >淇敼</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['system:vehicleAlertConfig:remove']"
+        >鍒犻櫎</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['system:vehicleAlertConfig:export']"
+        >瀵煎嚭</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <!-- 閰嶇疆璇存槑 -->
+    <el-alert
+      title="閰嶇疆璇存槑"
+      type="info"
+      :closable="false"
+      style="margin-bottom: 10px;">
+      <div slot="default">
+        <p>鈥� <strong>鍏ㄥ眬閰嶇疆</strong>锛氶�傜敤浜庢墍鏈夎溅杈嗙殑榛樿閰嶇疆</p>
+        <p>鈥� <strong>閮ㄩ棬閰嶇疆</strong>锛氶拡瀵圭壒瀹氶儴闂ㄧ殑杞﹁締閰嶇疆锛屼紭鍏堢骇楂樹簬鍏ㄥ眬閰嶇疆</p>
+        <p>鈥� <strong>杞﹁締閰嶇疆</strong>锛氶拡瀵圭壒瀹氳溅杈嗙殑涓�у寲閰嶇疆锛屼紭鍏堢骇鏈�楂�</p>
+        <p>鈥� <strong>閫氱煡鐢ㄦ埛</strong>锛氬涓敤鎴稩D鐢ㄨ嫳鏂囬�楀彿鍒嗛殧锛屽锛�1,2,3</p>
+      </div>
+    </el-alert>
+
+    <el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="閰嶇疆ID" align="center" prop="configId" width="80" />
+      <el-table-column label="閰嶇疆绫诲瀷" align="center" prop="configType" width="100">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.configType === 'GLOBAL'" type="primary">鍏ㄥ眬閰嶇疆</el-tag>
+          <el-tag v-else-if="scope.row.configType === 'DEPT'" type="success">閮ㄩ棬閰嶇疆</el-tag>
+          <el-tag v-else type="warning">杞﹁締閰嶇疆</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="閮ㄩ棬/杞﹁締" align="center" prop="targetName" width="150">
+        <template slot-scope="scope">
+          <span v-if="scope.row.configType === 'GLOBAL'">-</span>
+          <span v-else>{{ scope.row.targetName || '-' }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="閲岀▼闃堝��(km)" align="center" prop="mileageThreshold" width="120">
+        <template slot-scope="scope">
+          <el-tag type="danger">{{ scope.row.mileageThreshold }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="姣忔棩鍛婅娆℃暟" align="center" prop="dailyAlertLimit" width="120" />
+      <el-table-column label="鍛婅闂撮殧(鍒嗛挓)" align="center" prop="alertInterval" width="120" />
+      <el-table-column label="閫氱煡鐢ㄦ埛" align="center" prop="notifyUserIds" width="120" :show-overflow-tooltip="true">
+        <template slot-scope="scope">
+          <span v-if="scope.row.notifyUserIds">{{ scope.row.notifyUserIds }}</span>
+          <span v-else style="color: #909399;">鏈厤缃�</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="鐘舵��" align="center" prop="status" width="80">
+        <template slot-scope="scope">
+          <el-switch
+            v-model="scope.row.status"
+            active-value="0"
+            inactive-value="1"
+            @change="handleStatusChange(scope.row)"
+          ></el-switch>
+        </template>
+      </el-table-column>
+      <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="澶囨敞" align="center" prop="remark" :show-overflow-tooltip="true" />
+      <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width" width="150" fixed="right">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['system:vehicleAlertConfig:edit']"
+          >淇敼</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['system:vehicleAlertConfig:remove']"
+          >鍒犻櫎</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 娣诲姞鎴栦慨鏀硅溅杈嗗憡璀﹂厤缃璇濇 -->
+    <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+        <el-form-item label="閰嶇疆绫诲瀷" prop="configType">
+          <el-radio-group v-model="form.configType" @change="handleConfigTypeChange">
+            <el-radio label="GLOBAL">鍏ㄥ眬閰嶇疆</el-radio>
+            <el-radio label="DEPT">閮ㄩ棬閰嶇疆</el-radio>
+            <el-radio label="VEHICLE">杞﹁締閰嶇疆</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="閮ㄩ棬" prop="deptId" v-if="form.configType === 'DEPT'">
+          <el-select v-model="form.deptId" placeholder="璇烽�夋嫨閮ㄩ棬" clearable style="width: 100%">
+            <el-option
+              v-for="dept in deptList"
+              :key="dept.deptId"
+              :label="dept.deptName"
+              :value="dept.deptId"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="杞﹁締" prop="vehicleId" v-if="form.configType === 'VEHICLE'">
+          <el-select v-model="form.vehicleId" placeholder="璇烽�夋嫨杞﹁締" clearable filterable style="width: 100%">
+            <el-option
+              v-for="vehicle in vehicleList"
+              :key="vehicle.vehicleId"
+              :label="vehicle.vehicleNo"
+              :value="vehicle.vehicleId"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="閲岀▼闃堝��(km)" prop="mileageThreshold">
+          <el-input-number
+            v-model="form.mileageThreshold"
+            :min="1"
+            :max="1000"
+            :step="1"
+            placeholder="璇疯緭鍏ラ噷绋嬮槇鍊�"
+            style="width: 100%"
+          />
+          <span style="color: #909399; font-size: 12px;">杞﹁締鏈粦瀹氫换鍔℃椂锛岃秴杩囨閲岀▼灏嗚Е鍙戝憡璀�</span>
+        </el-form-item>
+        <el-form-item label="姣忔棩鍛婅娆℃暟" prop="dailyAlertLimit">
+          <el-input-number
+            v-model="form.dailyAlertLimit"
+            :min="1"
+            :max="100"
+            :step="1"
+            placeholder="璇疯緭鍏ユ瘡鏃ュ憡璀︽鏁伴檺鍒�"
+            style="width: 100%"
+          />
+          <span style="color: #909399; font-size: 12px;">姣忚締杞︽瘡澶╂渶澶氬憡璀︽鏁�</span>
+        </el-form-item>
+        <el-form-item label="鍛婅闂撮殧(鍒嗛挓)" prop="alertInterval">
+          <el-input-number
+            v-model="form.alertInterval"
+            :min="1"
+            :max="1440"
+            :step="1"
+            placeholder="璇疯緭鍏ュ憡璀﹂棿闅旀椂闂�"
+            style="width: 100%"
+          />
+          <span style="color: #909399; font-size: 12px;">涓ゆ鍛婅涔嬮棿鐨勬渶灏忔椂闂撮棿闅�</span>
+        </el-form-item>
+        <el-form-item label="閫氱煡鐢ㄦ埛ID" prop="notifyUserIds">
+          <el-input
+            v-model="form.notifyUserIds"
+            type="textarea"
+            :rows="3"
+            placeholder="璇疯緭鍏ラ�氱煡鐢ㄦ埛ID鍒楄〃锛屽涓敤鑻辨枃閫楀彿鍒嗛殧锛屽锛�1,2,3"
+          />
+        </el-form-item>
+        <el-form-item label="鐘舵��" prop="status">
+          <el-radio-group v-model="form.status">
+            <el-radio label="0">鍚敤</el-radio>
+            <el-radio label="1">鍋滅敤</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+        <el-button @click="cancel">鍙� 娑�</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listVehicleAlertConfig, getVehicleAlertConfig, delVehicleAlertConfig, addVehicleAlertConfig, updateVehicleAlertConfig } from "@/api/system/vehicleAlertConfig";
+import { listDept } from "@/api/system/dept";
+import { listVehicle } from "@/api/system/vehicle";
+
+export default {
+  name: "VehicleAlertConfig",
+  data() {
+    return {
+      // 閬僵灞�
+      loading: true,
+      // 閫変腑鏁扮粍
+      ids: [],
+      // 闈炲崟涓鐢�
+      single: true,
+      // 闈炲涓鐢�
+      multiple: true,
+      // 鏄剧ず鎼滅储鏉′欢
+      showSearch: true,
+      // 鎬绘潯鏁�
+      total: 0,
+      // 杞﹁締鍛婅閰嶇疆琛ㄦ牸鏁版嵁
+      configList: [],
+      // 閮ㄩ棬鍒楄〃
+      deptList: [],
+      // 杞﹁締鍒楄〃
+      vehicleList: [],
+      // 寮瑰嚭灞傛爣棰�
+      title: "",
+      // 鏄惁鏄剧ず寮瑰嚭灞�
+      open: false,
+      // 鏌ヨ鍙傛暟
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        configType: null,
+        deptId: null,
+        vehicleId: null,
+        status: null
+      },
+      // 琛ㄥ崟鍙傛暟
+      form: {},
+      // 琛ㄥ崟鏍¢獙
+      rules: {
+        configType: [
+          { required: true, message: "閰嶇疆绫诲瀷涓嶈兘涓虹┖", trigger: "change" }
+        ],
+        deptId: [
+          { required: true, message: "閮ㄩ棬涓嶈兘涓虹┖", trigger: "change" }
+        ],
+        vehicleId: [
+          { required: true, message: "杞﹁締涓嶈兘涓虹┖", trigger: "change" }
+        ],
+        mileageThreshold: [
+          { required: true, message: "閲岀▼闃堝�间笉鑳戒负绌�", trigger: "blur" }
+        ],
+        dailyAlertLimit: [
+          { required: true, message: "姣忔棩鍛婅娆℃暟涓嶈兘涓虹┖", trigger: "blur" }
+        ],
+        alertInterval: [
+          { required: true, message: "鍛婅闂撮殧涓嶈兘涓虹┖", trigger: "blur" }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+    this.getDeptList();
+    this.getVehicleList();
+  },
+  methods: {
+    /** 鏌ヨ杞﹁締鍛婅閰嶇疆鍒楄〃 */
+    getList() {
+      this.loading = true;
+      listVehicleAlertConfig(this.queryParams).then(response => {
+        this.configList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 鏌ヨ閮ㄩ棬鍒楄〃 */
+    getDeptList() {
+      listDept().then(response => {
+        this.deptList = response.data;
+      });
+    },
+    /** 鏌ヨ杞﹁締鍒楄〃 */
+    getVehicleList() {
+      listVehicle({ pageNum: 1, pageSize: 10000 }).then(response => {
+        this.vehicleList = response.rows || [];
+      });
+    },
+    // 鍙栨秷鎸夐挳
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 琛ㄥ崟閲嶇疆
+    reset() {
+      this.form = {
+        configId: null,
+        configType: "GLOBAL",
+        deptId: null,
+        vehicleId: null,
+        mileageThreshold: 10,
+        dailyAlertLimit: 5,
+        alertInterval: 5,
+        notifyUserIds: null,
+        status: "0",
+        remark: null
+      };
+      this.resetForm("form");
+    },
+    /** 鎼滅储鎸夐挳鎿嶄綔 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 閲嶇疆鎸夐挳鎿嶄綔 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 澶氶�夋閫変腑鏁版嵁
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.configId);
+      this.single = selection.length !== 1;
+      this.multiple = !selection.length;
+    },
+    /** 閰嶇疆绫诲瀷鏀瑰彉 */
+    handleConfigTypeChange(value) {
+      if (value === 'GLOBAL') {
+        this.form.deptId = null;
+        this.form.vehicleId = null;
+      } else if (value === 'DEPT') {
+        this.form.vehicleId = null;
+      } else if (value === 'VEHICLE') {
+        this.form.deptId = null;
+      }
+    },
+    /** 鐘舵�佷慨鏀� */
+    handleStatusChange(row) {
+      let text = row.status === "0" ? "鍚敤" : "鍋滅敤";
+      this.$modal.confirm('纭瑕�"' + text + '""' + row.configId + '"閰嶇疆鍚楋紵').then(function() {
+        return updateVehicleAlertConfig(row);
+      }).then(() => {
+        this.$modal.msgSuccess(text + "鎴愬姛");
+      }).catch(function() {
+        row.status = row.status === "0" ? "1" : "0";
+      });
+    },
+    /** 鏂板鎸夐挳鎿嶄綔 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "娣诲姞杞﹁締鍛婅閰嶇疆";
+    },
+    /** 淇敼鎸夐挳鎿嶄綔 */
+    handleUpdate(row) {
+      this.reset();
+      const configId = row.configId || this.ids[0];
+      getVehicleAlertConfig(configId).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "淇敼杞﹁締鍛婅閰嶇疆";
+      });
+    },
+    /** 鎻愪氦鎸夐挳 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          // 鏍规嵁閰嶇疆绫诲瀷娓呯┖涓嶉渶瑕佺殑瀛楁
+          if (this.form.configType === 'GLOBAL') {
+            this.form.deptId = null;
+            this.form.vehicleId = null;
+          } else if (this.form.configType === 'DEPT') {
+            this.form.vehicleId = null;
+          } else if (this.form.configType === 'VEHICLE') {
+            this.form.deptId = null;
+          }
+          
+          if (this.form.configId != null) {
+            updateVehicleAlertConfig(this.form).then(response => {
+              this.$modal.msgSuccess("淇敼鎴愬姛");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addVehicleAlertConfig(this.form).then(response => {
+              this.$modal.msgSuccess("鏂板鎴愬姛");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 鍒犻櫎鎸夐挳鎿嶄綔 */
+    handleDelete(row) {
+      const configIds = row.configId || this.ids;
+      this.$modal.confirm('鏄惁纭鍒犻櫎閰嶇疆缂栧彿涓�"' + configIds + '"鐨勬暟鎹」锛�').then(function() {
+        return delVehicleAlertConfig(configIds);
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+      }).catch(() => {});
+    },
+    /** 瀵煎嚭鎸夐挳鎿嶄綔 */
+    handleExport() {
+      this.download('system/vehicleAlertConfig/export', {
+        ...this.queryParams
+      }, `杞﹁締鍛婅閰嶇疆_${new Date().getTime()}.xlsx`)
+    }
+  }
+};
+</script>
+
+<style scoped>
+.mb8 {
+  margin-bottom: 8px;
+}
+</style>
diff --git a/ruoyi-ui/src/views/system/vehicleSync/index.vue b/ruoyi-ui/src/views/system/vehicleSync/index.vue
new file mode 100644
index 0000000..d7673fb
--- /dev/null
+++ b/ruoyi-ui/src/views/system/vehicleSync/index.vue
@@ -0,0 +1,245 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-card">
+      <div slot="header" class="clearfix">
+        <span>杞﹁締鍚屾绠$悊</span>
+        <el-button
+          style="float: right; padding: 3px 0"
+          type="text"
+          @click="handleRefresh"
+        >
+          <i class="el-icon-refresh"></i> 鍒锋柊
+        </el-button>
+      </div>
+
+      <!-- 璇存槑鎻愮ず -->
+      <el-alert
+        title="鍔熻兘璇存槑"
+        type="info"
+        :closable="false"
+        style="margin-bottom: 15px"
+      >
+        <div>
+          鏈〉闈㈢敤浜庢煡鐪嬫棫绯荤粺涓殑杞﹁締鏁版嵁锛屽苟鎵嬪姩鍚屾鏈悓姝ョ殑杞﹁締鍒版柊绯荤粺銆�<br/>
+          <strong>宸插悓姝�</strong>锛氳溅杈嗗凡瀛樺湪浜庢柊绯荤粺涓紱<strong>鏈悓姝�</strong>锛氳溅杈嗚繕鏈悓姝ワ紝鍙墜鍔ㄦ坊鍔犲埌鏂扮郴缁熴��
+        </div>
+      </el-alert>
+
+      <!-- 绛涢�夋潯浠� -->
+      <el-form :inline="true" class="filter-form">
+        <el-form-item label="鍚屾鐘舵��">
+          <el-select v-model="filterSynced" placeholder="璇烽�夋嫨" @change="handleFilter" clearable>
+            <el-option label="鍏ㄩ儴" :value="null" />
+            <el-option label="鏈悓姝�" :value="false" />
+            <el-option label="宸插悓姝�" :value="true" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+
+      <!-- 鏁版嵁琛ㄦ牸 -->
+      <el-table
+        v-loading="loading"
+        :data="filteredVehicleList"
+        style="width: 100%"
+        border
+      >
+        <el-table-column label="杞﹁締ID(CarID)" prop="carId" width="120" align="center" />
+        <el-table-column label="杞︾墝鍙�" prop="vehicleNo" width="150" align="center">
+          <template slot-scope="scope">
+            <el-tag>{{ scope.row.vehicleNo }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鍗曟嵁绫诲瀷缂栫爜" prop="carOrdClass" width="120" align="center" />
+        <el-table-column label="褰掑睘鍒嗗叕鍙�" prop="deptName" width="150" align="center" />
+        <el-table-column label="鍚屾鐘舵��" prop="synced" width="120" align="center">
+          <template slot-scope="scope">
+            <el-tag :type="scope.row.synced ? 'success' : 'warning'">
+              {{ scope.row.synced ? '宸插悓姝�' : '鏈悓姝�' }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鏂扮郴缁熻溅杈咺D" prop="vehicleId" width="130" align="center">
+          <template slot-scope="scope">
+            <span v-if="scope.row.vehicleId">{{ scope.row.vehicleId }}</span>
+            <span v-else style="color: #909399">-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template slot-scope="scope">
+            <el-button
+              v-if="!scope.row.synced"
+              size="mini"
+              type="primary"
+              icon="el-icon-plus"
+              @click="handleSync(scope.row)"
+              v-hasPermi="['system:vehicleSync:sync']"
+            >
+              鍚屾
+            </el-button>
+            <el-tag v-else type="info">宸插悓姝�</el-tag>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+
+    <!-- 鍚屾瀵硅瘽妗� -->
+    <el-dialog
+      title="鎵嬪姩鍚屾杞﹁締"
+      :visible.sync="syncDialogVisible"
+      width="500px"
+      append-to-body
+    >
+      <el-form ref="syncForm" :model="syncForm" :rules="syncRules" label-width="100px">
+        <el-form-item label="鏃х郴缁烮D" prop="carId">
+          <el-input v-model="syncForm.carId" disabled />
+        </el-form-item>
+        <el-form-item label="杞︾墝鍙�" prop="vehicleNo">
+          <el-input v-model="syncForm.vehicleNo" disabled />
+        </el-form-item>
+        <el-form-item label="褰掑睘鍒嗗叕鍙�" prop="deptId">
+          <el-select v-model="syncForm.deptId" placeholder="璇烽�夋嫨褰掑睘鍒嗗叕鍙�" clearable style="width: 100%">
+            <el-option
+              v-for="dept in deptOptions"
+              :key="dept.deptId"
+              :label="dept.deptName"
+              :value="dept.deptId"
+            />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="syncDialogVisible = false">鍙� 娑�</el-button>
+        <el-button type="primary" @click="submitSync" :loading="syncLoading">纭� 瀹�</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listVehicleSync, syncVehicle } from "@/api/system/vehicleSync";
+import { listDept } from "@/api/system/dept";
+
+export default {
+  name: "VehicleSync",
+  data() {
+    return {
+      // 鍔犺浇鐘舵��
+      loading: true,
+      // 杞﹁締鍒楄〃锛堝師濮嬫暟鎹級
+      vehicleList: [],
+      // 绛涢�夊悗鐨勮溅杈嗗垪琛�
+      filteredVehicleList: [],
+      // 绛涢�夋潯浠讹細鍚屾鐘舵��
+      filterSynced: null,
+      // 鍚屾瀵硅瘽妗嗘樉绀虹姸鎬�
+      syncDialogVisible: false,
+      // 鍚屾鍔犺浇鐘舵��
+      syncLoading: false,
+      // 鍚屾琛ㄥ崟
+      syncForm: {
+        carId: null,
+        vehicleNo: null,
+        deptId: null
+      },
+      // 閮ㄩ棬鏍戦�夐」
+      deptOptions: [],
+      // 琛ㄥ崟鏍¢獙瑙勫垯
+      syncRules: {
+        deptId: [
+          { required: true, message: "璇烽�夋嫨褰掑睘鍒嗗叕鍙�", trigger: "change" }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+    this.getDeptList();
+  },
+  methods: {
+    /** 鏌ヨ杞﹁締鍚屾鍒楄〃 */
+    getList() {
+      this.loading = true;
+      listVehicleSync().then(response => {
+        this.vehicleList = response.rows;
+        this.applyFilter();
+        this.loading = false;
+      }).catch(() => {
+        this.loading = false;
+      });
+    },
+    /** 搴旂敤绛涢�夋潯浠� */
+    applyFilter() {
+      if (this.filterSynced === null) {
+        // 鏄剧ず鍏ㄩ儴
+        this.filteredVehicleList = this.vehicleList;
+      } else {
+        // 鏍规嵁鍚屾鐘舵�佺瓫閫�
+        this.filteredVehicleList = this.vehicleList.filter(
+          item => item.synced === this.filterSynced
+        );
+      }
+    },
+    /** 绛涢�夋潯浠跺彉鍖� */
+    handleFilter() {
+      this.applyFilter();
+    },
+    /** 鑾峰彇閮ㄩ棬鍒楄〃锛堝彧鏄剧ず鍒嗗叕鍙革細parent_id=100锛� */
+    getDeptList() {
+      listDept({ parentId: 100 }).then(response => {
+        // 杩囨护鍑哄垎鍏徃锛坧arent_id=100鐨勯儴闂級
+        if (response.data) {
+          this.deptOptions = response.data.filter(dept => dept.parentId === "100");
+        } else {
+          this.deptOptions = [];
+        }
+      });
+    },
+    /** 鍒锋柊鍒楄〃 */
+    handleRefresh() {
+      this.getList();
+      this.$modal.msgSuccess("鍒锋柊鎴愬姛");
+    },
+    /** 鍚屾鎸夐挳鎿嶄綔 */
+    handleSync(row) {
+      this.syncForm = {
+        carId: row.carId,
+        vehicleNo: row.vehicleNo,
+        deptId: row.deptId || null
+      };
+      this.syncDialogVisible = true;
+      this.$nextTick(() => {
+        this.$refs["syncForm"].clearValidate();
+      });
+    },
+    /** 鎻愪氦鍚屾 */
+    submitSync() {
+      this.$refs["syncForm"].validate(valid => {
+        if (valid) {
+          this.syncLoading = true;
+          syncVehicle(this.syncForm).then(response => {
+            this.$modal.msgSuccess("鍚屾鎴愬姛");
+            this.syncDialogVisible = false;
+            this.syncLoading = false;
+            this.getList();
+          }).catch(() => {
+            this.syncLoading = false;
+          });
+        }
+      });
+    }
+  }
+};
+</script>
+
+<style scoped>
+.box-card {
+  margin-bottom: 20px;
+}
+
+.filter-form {
+  margin-bottom: 15px;
+  padding: 10px;
+  background-color: #f5f7fa;
+  border-radius: 4px;
+}
+</style>
diff --git a/sql/hospital_tokenizer_menu.sql b/sql/hospital_tokenizer_menu.sql
new file mode 100644
index 0000000..7b17246
--- /dev/null
+++ b/sql/hospital_tokenizer_menu.sql
@@ -0,0 +1,24 @@
+-- 鍖婚櫌鍒嗚瘝娴嬭瘯鑿滃崟SQL
+-- 鎵ц姝よ剼鏈悗锛岄渶瑕佺粰绠$悊鍛樿鑹插垎閰嶈彍鍗曟潈闄�
+
+-- 鏌ユ壘绯荤粺绠$悊鑿滃崟ID
+SELECT @parentId := menu_id FROM sys_menu WHERE menu_name = '绯荤粺绠$悊' AND parent_id = 0;
+
+-- 鎻掑叆鍖婚櫌绠$悊涓�绾ц彍鍗�
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark)
+VALUES
+('鍖婚櫌绠$悊', @parentId, 10, 'hospital', NULL, 1, 0, 'M', '0', '0', NULL, 'hospital', 'admin', sysdate(), '鍖婚櫌鍒嗚瘝绠$悊');
+
+-- 鑾峰彇鍒氭彃鍏ョ殑鍖婚櫌绠$悊鑿滃崟ID
+SELECT @hospitalMenuId := menu_id FROM sys_menu WHERE menu_name = '鍖婚櫌绠$悊' AND parent_id = @parentId;
+
+-- 鎻掑叆鍖婚櫌鍒嗚瘝娴嬭瘯浜岀骇鑿滃崟
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark)
+VALUES
+('鍖婚櫌鍒嗚瘝娴嬭瘯', @hospitalMenuId, 1, 'tokenizer', 'system/hospital/tokenizer', 1, 0, 'C', '0', '0', 'system:hospital:tokenizer', 'search', 'admin', sysdate(), '鍖婚櫌鍒嗚瘝涓庢悳绱㈡祴璇曞伐鍏�');
+
+-- 璇存槑锛�
+-- 1. 鎵ц姝よ剼鏈悗锛岃彍鍗曚細鍑虹幇鍦�"绯荤粺绠$悊"涓�
+-- 2. 闇�瑕佸湪"绯荤粺绠$悊 > 瑙掕壊绠$悊"涓粰鐩稿簲瑙掕壊鍒嗛厤"鍖婚櫌绠$悊"鑿滃崟鏉冮檺
+-- 3. 鏉冮檺鏍囪瘑锛歴ystem:hospital:tokenizer
+-- 4. 鑿滃崟璺緞锛氱郴缁熺鐞� > 鍖婚櫌绠$悊 > 鍖婚櫌鍒嗚瘝娴嬭瘯
diff --git a/sql/ocr_module_menu.sql b/sql/ocr_module_menu.sql
new file mode 100644
index 0000000..989076b
--- /dev/null
+++ b/sql/ocr_module_menu.sql
@@ -0,0 +1,82 @@
+-- OCR妯″潡瀹屾暣鑿滃崟SQL
+-- 鍖呭惈OCR绠$悊銆佹祴璇曘�侀厤缃拰缃戠粶璇婃柇鍔熻兘
+
+-- 鑾峰彇绯荤粺宸ュ叿鑿滃崟ID锛堥�氬父鏄�3锛�
+SET @parentMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = '绯荤粺宸ュ叿' AND parent_id = 0 LIMIT 1);
+
+-- 濡傛灉娌℃湁绯荤粺宸ュ叿鑿滃崟锛屽垯浣跨敤绯荤粺绠$悊锛坧arent_id=1锛�
+SET @parentMenuId = IFNULL(@parentMenuId, 1);
+
+-- 鎻掑叆OCR绠$悊鑿滃崟
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT 'OCR绠$悊', @parentMenuId, 9, 'ocr', '', 1, 0, 'M', '0', '0', '', 'camera', 'admin', NOW(), '', NULL, 'OCR鏈嶅姟绠$悊鑿滃崟'
+FROM DUAL
+WHERE NOT EXISTS (
+    SELECT 1 FROM sys_menu WHERE menu_name = 'OCR绠$悊' AND perms = ''
+);
+
+-- 鑾峰彇OCR绠$悊鑿滃崟ID
+SET @ocrParentMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = 'OCR绠$悊' LIMIT 1);
+
+-- 鎻掑叆OCR娴嬭瘯瀛愯彍鍗�
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT 'OCR娴嬭瘯', @ocrParentMenuId, 1, 'ocr', 'system/ocr/index', 1, 0, 'C', '0', '0', 'system:ocr:test', 'eye-open', 'admin', NOW(), '', NULL, 'OCR鍥惧儚璇嗗埆娴嬭瘯椤甸潰'
+FROM DUAL
+WHERE NOT EXISTS (
+    SELECT 1 FROM sys_menu WHERE menu_name = 'OCR娴嬭瘯' AND perms = 'system:ocr:test'
+);
+
+-- 鎻掑叆OCR閰嶇疆瀛愯彍鍗�
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT 'OCR閰嶇疆', @ocrParentMenuId, 2, 'config', 'system/ocr/config', 1, 0, 'C', '0', '0', 'system:ocr:config', 'setting', 'admin', NOW(), '', NULL, 'OCR鏈嶅姟閰嶇疆椤甸潰'
+FROM DUAL
+WHERE NOT EXISTS (
+    SELECT 1 FROM sys_menu WHERE menu_name = 'OCR閰嶇疆' AND perms = 'system:ocr:config'
+);
+
+-- 鎻掑叆OCR璇嗗埆鏉冮檺鎸夐挳
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT 'OCR璇嗗埆', (SELECT menu_id FROM sys_menu WHERE perms = 'system:ocr:test' LIMIT 1), 1, '#', '', 1, 0, 'F', '0', '0', 'system:ocr:recognize', '#', 'admin', NOW(), '', NULL, 'OCR鍥惧儚璇嗗埆鏉冮檺'
+FROM DUAL
+WHERE NOT EXISTS (
+    SELECT 1 FROM sys_menu WHERE perms = 'system:ocr:recognize'
+);
+
+-- 鎻掑叆OCR鏈嶅姟鎻愪緵鍟嗘煡璇㈡潈闄愭寜閽�
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT 'OCR鏈嶅姟鍟嗘煡璇�', (SELECT menu_id FROM sys_menu WHERE perms = 'system:ocr:test' LIMIT 1), 2, '#', '', 1, 0, 'F', '0', '0', 'system:ocr:providers', '#', 'admin', NOW(), '', NULL, 'OCR鏈嶅姟鎻愪緵鍟嗘煡璇㈡潈闄�'
+FROM DUAL
+WHERE NOT EXISTS (
+    SELECT 1 FROM sys_menu WHERE perms = 'system:ocr:providers'
+);
+
+-- 鎻掑叆缃戠粶璇婃柇鑿滃崟
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '缃戠粶璇婃柇', @parentMenuId, 10, 'diag', '', 1, 0, 'M', '0', '0', '', 'link', 'admin', NOW(), '', NULL, '缃戠粶璇婃柇绠$悊鑿滃崟'
+FROM DUAL
+WHERE NOT EXISTS (
+    SELECT 1 FROM sys_menu WHERE menu_name = '缃戠粶璇婃柇' AND perms = ''
+);
+
+-- 鎻掑叆OCR缃戠粶璇婃柇瀛愯彍鍗�
+SET @diagParentMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = '缃戠粶璇婃柇' LIMIT 1);
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT 'OCR杩炴帴璇婃柇', @diagParentMenuId, 1, 'ocrConnection', 'system/diag/ocrConnection', 1, 0, 'C', '0', '0', 'system:diag:ocr', 'monitor', 'admin', NOW(), '', NULL, 'OCR鏈嶅姟杩炴帴璇婃柇椤甸潰'
+FROM DUAL
+WHERE NOT EXISTS (
+    SELECT 1 FROM sys_menu WHERE menu_name = 'OCR杩炴帴璇婃柇' AND perms = 'system:diag:ocr'
+);
+
+-- 鏌ヨ缁撴灉
+SELECT 
+    menu_id,
+    menu_name,
+    parent_id,
+    path,
+    component,
+    perms,
+    icon,
+    '鑿滃崟娣诲姞鎴愬姛' AS status
+FROM sys_menu 
+WHERE menu_name IN ('OCR绠$悊', 'OCR娴嬭瘯', 'OCR閰嶇疆', 'OCR璇嗗埆', 'OCR鏈嶅姟鍟嗘煡璇�', '缃戠粶璇婃柇', 'OCR杩炴帴璇婃柇')
+ORDER BY parent_id, order_num;
diff --git a/sql/ocr_test_menu.sql b/sql/ocr_test_menu.sql
new file mode 100644
index 0000000..2990e39
--- /dev/null
+++ b/sql/ocr_test_menu.sql
@@ -0,0 +1,41 @@
+-- OCR娴嬭瘯椤甸潰鑿滃崟SQL
+-- 娉ㄦ剰锛氳鏍规嵁瀹為檯鎯呭喌璋冩暣parent_id锛堢埗鑿滃崟ID锛�
+
+-- 鑾峰彇绯荤粺宸ュ叿鑿滃崟ID锛堥�氬父鏄�3锛�
+SET @parentMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = '绯荤粺宸ュ叿' AND parent_id = 0 LIMIT 1);
+
+-- 濡傛灉娌℃湁绯荤粺宸ュ叿鑿滃崟锛屽垯浣跨敤绯荤粺绠$悊锛坧arent_id=1锛�
+SET @parentMenuId = IFNULL(@parentMenuId, 1);
+
+-- 鎻掑叆OCR娴嬭瘯鑿滃崟
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT 'OCR娴嬭瘯', @parentMenuId, 10, 'ocr', 'system/ocr/index', 1, 0, 'C', '0', '0', 'system:ocr:test', 'eye-open', 'admin', NOW(), '', NULL, 'OCR鍥惧儚璇嗗埆娴嬭瘯椤甸潰'
+FROM DUAL
+WHERE NOT EXISTS (
+    SELECT 1 FROM sys_menu WHERE menu_name = 'OCR娴嬭瘯' AND perms = 'system:ocr:test'
+);
+
+-- 鑾峰彇鍒氭彃鍏ョ殑OCR娴嬭瘯鑿滃崟ID
+SET @ocrMenuId = (SELECT menu_id FROM sys_menu WHERE perms = 'system:ocr:test' LIMIT 1);
+
+-- 鎻掑叆OCR璇嗗埆鏉冮檺鎸夐挳
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT 'OCR璇嗗埆', @ocrMenuId, 1, '#', '', 1, 0, 'F', '0', '0', 'system:ocr:recognize', '#', 'admin', NOW(), '', NULL, 'OCR鍥惧儚璇嗗埆鏉冮檺'
+FROM DUAL
+WHERE NOT EXISTS (
+    SELECT 1 FROM sys_menu WHERE perms = 'system:ocr:recognize'
+);
+
+-- 鏌ヨ缁撴灉
+SELECT 
+    menu_id,
+    menu_name,
+    parent_id,
+    path,
+    component,
+    perms,
+    icon,
+    '鑿滃崟娣诲姞鎴愬姛' AS status
+FROM sys_menu 
+WHERE menu_name IN ('OCR娴嬭瘯', 'OCR璇嗗埆')
+ORDER BY menu_id;
diff --git a/sql/tb_hosp_data_add_keywords.sql b/sql/tb_hosp_data_add_keywords.sql
new file mode 100644
index 0000000..312f964
--- /dev/null
+++ b/sql/tb_hosp_data_add_keywords.sql
@@ -0,0 +1,9 @@
+-- 涓哄尰闄㈡暟鎹〃娣诲姞鍒嗚瘝瀛楁
+-- 鎵ц鏃堕棿锛�2026-01-20
+
+-- 娣诲姞鍒嗚瘝瀛楁锛堝瓨鍌ㄤ互閫楀彿鍒嗛殧鐨勫叧閿瘝锛�
+ALTER TABLE `tb_hosp_data` 
+ADD COLUMN `hosp_keywords` varchar(4000) DEFAULT NULL COMMENT '鍖婚櫌淇℃伅鍒嗚瘝锛堥�楀彿鍒嗛殧锛�' AFTER `hosp_level`;
+
+-- 涓哄垎璇嶅瓧娈垫坊鍔犵储寮曚互鎻愬崌鎼滅储鎬ц兘
+CREATE INDEX idx_hosp_keywords ON tb_hosp_data(hosp_keywords);
diff --git a/sql/vehicle_abnormal_alert.sql b/sql/vehicle_abnormal_alert.sql
new file mode 100644
index 0000000..39e4eaf
--- /dev/null
+++ b/sql/vehicle_abnormal_alert.sql
@@ -0,0 +1,183 @@
+-- 杞﹁締寮傚父杩愯鍛婅璁板綍琛�
+CREATE TABLE `tb_vehicle_abnormal_alert` (
+  `alert_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '鍛婅ID',
+  `vehicle_id` bigint(20) NOT NULL COMMENT '杞﹁締ID',
+  `vehicle_no` varchar(20) DEFAULT NULL COMMENT '杞︾墝鍙�',
+  `alert_date` date NOT NULL COMMENT '鍛婅鏃ユ湡',
+  `alert_time` datetime NOT NULL COMMENT '鍛婅鏃堕棿',
+  `mileage` decimal(10,3) DEFAULT 0.000 COMMENT '绱杩愯鍏噷鏁�(鍏噷)',
+  `alert_type` varchar(20) DEFAULT 'NO_TASK_MILEAGE' COMMENT '鍛婅绫诲瀷(NO_TASK_MILEAGE-鏃犱换鍔¤秴鍏噷)',
+  `alert_reason` varchar(500) DEFAULT NULL COMMENT '鍛婅鍘熷洜鎻忚堪',
+  `start_time` datetime DEFAULT NULL COMMENT '寮�濮嬭繍琛屾椂闂�',
+  `end_time` datetime DEFAULT NULL COMMENT '缁撴潫杩愯鏃堕棿',
+  `alert_count` int(11) DEFAULT 1 COMMENT '褰撴棩鍛婅娆℃暟',
+  `status` char(1) DEFAULT '0' COMMENT '鐘舵�侊紙0-鏈鐞� 1-宸插鐞� 2-宸插拷鐣ワ級',
+  `handler_id` bigint(20) DEFAULT NULL COMMENT '澶勭悊浜篒D',
+  `handler_name` varchar(64) DEFAULT NULL COMMENT '澶勭悊浜哄鍚�',
+  `handle_time` datetime DEFAULT NULL COMMENT '澶勭悊鏃堕棿',
+  `handle_remark` varchar(500) DEFAULT NULL COMMENT '澶勭悊澶囨敞',
+  `notify_status` char(1) DEFAULT '0' COMMENT '閫氱煡鐘舵�侊紙0-鏈彂閫� 1-宸插彂閫� 2-鍙戦�佸け璐ワ級',
+  `notify_time` datetime DEFAULT NULL COMMENT '閫氱煡鏃堕棿',
+  `notify_users` varchar(500) DEFAULT NULL COMMENT '閫氱煡鐢ㄦ埛ID鍒楄〃锛堥�楀彿鍒嗛殧锛�',
+  `dept_id` bigint(20) DEFAULT NULL COMMENT '褰掑睘閮ㄩ棬ID',
+  `dept_name` varchar(100) DEFAULT NULL COMMENT '褰掑睘閮ㄩ棬鍚嶇О',
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+  PRIMARY KEY (`alert_id`),
+  KEY `idx_vehicle_id` (`vehicle_id`),
+  KEY `idx_alert_date` (`alert_date`),
+  KEY `idx_alert_time` (`alert_time`),
+  KEY `idx_vehicle_date` (`vehicle_id`, `alert_date`),
+  KEY `idx_dept_id` (`dept_id`),
+  KEY `idx_status` (`status`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='杞﹁締寮傚父杩愯鍛婅璁板綍琛�';
+
+-- 杞﹁締寮傚父鍛婅閰嶇疆琛�
+DROP TABLE IF EXISTS `tb_vehicle_alert_config`;
+CREATE TABLE `tb_vehicle_alert_config` (
+  `config_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '閰嶇疆ID',
+  `config_type` varchar(50) NOT NULL COMMENT '閰嶇疆绫诲瀷(GLOBAL-鍏ㄥ眬/DEPT-閮ㄩ棬/VEHICLE-杞﹁締)',
+  `dept_id` bigint(20) DEFAULT NULL COMMENT '閮ㄩ棬ID锛堥儴闂ㄩ厤缃椂浣跨敤锛�',
+  `vehicle_id` bigint(20) DEFAULT NULL COMMENT '杞﹁締ID锛堣溅杈嗛厤缃椂浣跨敤锛�',
+  `mileage_threshold` decimal(10,3) DEFAULT 10.000 COMMENT '鍏噷鏁板憡璀﹂槇鍊�(鍏噷)',
+  `daily_alert_limit` int(11) DEFAULT 5 COMMENT '姣忔棩鏈�澶у憡璀︽鏁�',
+  `alert_interval` int(11) DEFAULT 5 COMMENT '鍛婅闂撮殧鏃堕棿(鍒嗛挓)',
+  `notify_user_ids` varchar(1000) DEFAULT NULL COMMENT '閫氱煡鐢ㄦ埛ID鍒楄〃锛堥�楀彿鍒嗛殧锛�',
+  `status` char(1) DEFAULT '0' COMMENT '鐘舵�侊紙0-鍚敤 1-鍋滅敤锛�',
+  `create_by` varchar(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+  `update_by` varchar(64) DEFAULT '' COMMENT '鏇存柊鑰�',
+  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+  `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+  PRIMARY KEY (`config_id`),
+  UNIQUE KEY `uk_vehicle_config` (`config_type`, `vehicle_id`),
+  UNIQUE KEY `uk_dept_config` (`config_type`, `dept_id`),
+  KEY `idx_dept_id` (`dept_id`),
+  KEY `idx_vehicle_id` (`vehicle_id`),
+  KEY `idx_status` (`status`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='杞﹁締寮傚父鍛婅閰嶇疆琛�';
+
+-- 鎻掑叆鍏ㄥ眬榛樿閰嶇疆
+INSERT INTO `tb_vehicle_alert_config` (
+  `config_type`, `dept_id`, `vehicle_id`, `mileage_threshold`, `daily_alert_limit`, 
+  `alert_interval`, `status`, `create_by`, `remark`
+) VALUES (
+  'GLOBAL', NULL, NULL, 10.000, 5, 5, '0', 'admin', '鍏ㄥ眬榛樿閰嶇疆锛氳溅杈嗘棤浠诲姟瓒�10鍏噷鍛婅锛屾瘡澶╂渶澶�5娆★紝闂撮殧5鍒嗛挓'
+);
+
+-- 娣诲姞绯荤粺閰嶇疆鍙傛暟
+INSERT INTO sys_config (config_name, config_key, config_value, config_type, remark, create_by, create_time) 
+VALUES ('杞﹁締寮傚父鍛婅鍚敤寮�鍏�', 'vehicle.alert.enabled', 'true', 'Y', '鎺у埗杞﹁締寮傚父杩愯鍛婅鍔熻兘鐨勬�诲紑鍏炽�倀rue=鍚敤锛宖alse=绂佺敤', 'admin', NOW())
+ON DUPLICATE KEY UPDATE config_value = config_value;
+
+INSERT INTO sys_config (config_name, config_key, config_value, config_type, remark, create_by, create_time) 
+VALUES ('杞﹁締寮傚父鍛婅鍏噷鏁伴槇鍊�', 'vehicle.alert.mileage.threshold', '10', 'Y', '杞﹁締鏃犱换鍔¤繍琛岃秴杩囪鍏噷鏁版椂瑙﹀彂鍛婅锛堝崟浣嶏細鍏噷锛�', 'admin', NOW())
+ON DUPLICATE KEY UPDATE config_value = config_value;
+
+INSERT INTO sys_config (config_name, config_key, config_value, config_type, remark, create_by, create_time) 
+VALUES ('杞﹁締寮傚父鍛婅姣忔棩娆℃暟闄愬埗', 'vehicle.alert.daily.limit', '5', 'Y', '姣忓彴杞︽瘡澶╂渶澶氬憡璀︽鏁�', 'admin', NOW())
+ON DUPLICATE KEY UPDATE config_value = config_value;
+
+INSERT INTO sys_config (config_name, config_key, config_value, config_type, remark, create_by, create_time) 
+VALUES ('杞﹁締寮傚父鍛婅闂撮殧鏃堕棿', 'vehicle.alert.interval.minutes', '5', 'Y', '鍚屼竴杞﹁締涓ゆ鍛婅涔嬮棿鐨勬渶灏忛棿闅旀椂闂达紙鍗曚綅锛氬垎閽燂級', 'admin', NOW())
+ON DUPLICATE KEY UPDATE config_value = config_value;
+
+INSERT INTO sys_config (config_name, config_key, config_value, config_type, remark, create_by, create_time) 
+VALUES ('杞﹁締寮傚父鍛婅閫氱煡鐢ㄦ埛', 'vehicle.alert.notify.users', '', 'Y', '鎺ユ敹鍛婅閫氱煡鐨勭敤鎴稩D鍒楄〃锛屽涓敤鎴风敤閫楀彿鍒嗛殧銆備负绌烘椂鏍规嵁杞﹁締褰掑睘閮ㄩ棬鍙戦�佺粰鍒嗗叕鍙歌礋璐d汉', 'admin', NOW())
+ON DUPLICATE KEY UPDATE config_value = config_value;
+
+INSERT INTO sys_config (config_name, config_key, config_value, config_type, remark, create_by, create_time) 
+VALUES ('杞﹁締寮傚父鍛婅鐩戞帶鏃堕棿绐楀彛', 'vehicle.alert.time.window', '10', 'Y', '鐩戞帶鏃堕棿绐楀彛锛堝崟浣嶏細鍒嗛挓锛夛紝鐢ㄤ簬璁$畻杞﹁締鍦ㄨ鏃堕棿绐楀彛鍐呯殑杩愯鍏噷鏁�', 'admin', NOW())
+ON DUPLICATE KEY UPDATE config_value = config_value;
+
+-- 鍒涘缓瀹氭椂浠诲姟
+INSERT INTO sys_job (job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, update_by, update_time, remark) 
+VALUES ('杞﹁締寮傚父杩愯鐩戞帶浠诲姟', 'DEFAULT', 'vehicleAbnormalAlertTask.monitorVehicleAbnormalRunning()', '0 */5 * * * ?', '3', '0', '1', 'admin', NOW(), 'admin', NOW(), '姣�5鍒嗛挓鎵ц涓�娆★紝鐩戞帶杞﹁締鏃犱换鍔¤秴鍏噷杩愯鎯呭喌')
+ON DUPLICATE KEY UPDATE invoke_target = invoke_target;
+
+-- =====================================================
+-- 鑿滃崟鏉冮檺閰嶇疆
+-- =====================================================
+
+-- 1. 鍒涘缓杞﹁締鐩戞帶鐖惰彍鍗曪紙濡傛灉涓嶅瓨鍦級
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '杞﹁締鐩戞帶', 0, 5, 'vehicle-monitor', NULL, 1, 0, 'M', '0', '0', '', 'monitor', 'admin', NOW(), 'admin', NOW(), '杞﹁締鐩戞帶绠$悊鐩綍'
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_name = '杞﹁締鐩戞帶' AND menu_type = 'M');
+
+-- 鑾峰彇杞﹁締鐩戞帶鐖惰彍鍗旾D
+SET @vehicleMonitorMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = '杞﹁締鐩戞帶' AND menu_type = 'M' LIMIT 1);
+
+-- 2. 鍒涘缓杞﹁締寮傚父鍛婅鑿滃崟
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '杞﹁締寮傚父鍛婅', @vehicleMonitorMenuId, 1, 'vehicleAlert', 'system/vehicleAlert/index', 1, 0, 'C', '0', '0', 'system:vehicleAlert:list', 'warning', 'admin', NOW(), 'admin', NOW(), '杞﹁締寮傚父杩愯鍛婅绠$悊'
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_name = '杞﹁締寮傚父鍛婅' AND perms = 'system:vehicleAlert:list');
+
+-- 鑾峰彇杞﹁締寮傚父鍛婅鑿滃崟ID
+SET @vehicleAlertMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = '杞﹁締寮傚父鍛婅' AND perms = 'system:vehicleAlert:list' LIMIT 1);
+
+-- 3. 鍒涘缓杞﹁締寮傚父鍛婅鍔熻兘鎸夐挳
+-- 3.1 鏌ヨ鎸夐挳
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '鍛婅鏌ヨ', @vehicleAlertMenuId, 1, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlert:query', '#', 'admin', NOW(), 'admin', NOW(), ''
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlert:query');
+
+-- 3.2 澶勭悊鍛婅鎸夐挳
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '澶勭悊鍛婅', @vehicleAlertMenuId, 2, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlert:handle', '#', 'admin', NOW(), 'admin', NOW(), ''
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlert:handle');
+
+-- 3.3 鍒犻櫎鍛婅鎸夐挳
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '鍒犻櫎鍛婅', @vehicleAlertMenuId, 3, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlert:remove', '#', 'admin', NOW(), 'admin', NOW(), ''
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlert:remove');
+
+-- 3.4 瀵煎嚭鍛婅鎸夐挳
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '瀵煎嚭鍛婅', @vehicleAlertMenuId, 4, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlert:export', '#', 'admin', NOW(), 'admin', NOW(), ''
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlert:export');
+
+-- 4. 鍒涘缓鍛婅閰嶇疆绠$悊鑿滃崟
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '鍛婅閰嶇疆绠$悊', @vehicleMonitorMenuId, 2, 'vehicleAlertConfig', 'system/vehicleAlertConfig/index', 1, 0, 'C', '0', '0', 'system:vehicleAlertConfig:list', 'edit', 'admin', NOW(), 'admin', NOW(), '杞﹁締鍛婅閰嶇疆绠$悊'
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_name = '鍛婅閰嶇疆绠$悊' AND perms = 'system:vehicleAlertConfig:list');
+
+-- 鑾峰彇鍛婅閰嶇疆绠$悊鑿滃崟ID
+SET @vehicleAlertConfigMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = '鍛婅閰嶇疆绠$悊' AND perms = 'system:vehicleAlertConfig:list' LIMIT 1);
+
+-- 5. 鍒涘缓鍛婅閰嶇疆绠$悊鍔熻兘鎸夐挳
+-- 5.1 鏌ヨ鎸夐挳
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '閰嶇疆鏌ヨ', @vehicleAlertConfigMenuId, 1, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlertConfig:query', '#', 'admin', NOW(), 'admin', NOW(), ''
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlertConfig:query');
+
+-- 5.2 鏂板鎸夐挳
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '鏂板閰嶇疆', @vehicleAlertConfigMenuId, 2, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlertConfig:add', '#', 'admin', NOW(), 'admin', NOW(), ''
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlertConfig:add');
+
+-- 5.3 淇敼鎸夐挳
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '淇敼閰嶇疆', @vehicleAlertConfigMenuId, 3, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlertConfig:edit', '#', 'admin', NOW(), 'admin', NOW(), ''
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlertConfig:edit');
+
+-- 5.4 鍒犻櫎鎸夐挳
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '鍒犻櫎閰嶇疆', @vehicleAlertConfigMenuId, 4, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlertConfig:remove', '#', 'admin', NOW(), 'admin', NOW(), ''
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlertConfig:remove');
+
+-- 5.5 瀵煎嚭鎸夐挳
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '瀵煎嚭閰嶇疆', @vehicleAlertConfigMenuId, 5, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlertConfig:export', '#', 'admin', NOW(), 'admin', NOW(), ''
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlertConfig:export');
diff --git a/sql/vehicle_abnormal_alert_upgrade.sql b/sql/vehicle_abnormal_alert_upgrade.sql
new file mode 100644
index 0000000..27e379f
--- /dev/null
+++ b/sql/vehicle_abnormal_alert_upgrade.sql
@@ -0,0 +1,150 @@
+-- =====================================================
+-- 杞﹁締寮傚父杩愯鐩戞帶鍛婅鍔熻兘 - 鏁版嵁搴撳崌绾ц剼鏈�
+-- 鐢ㄤ簬鏇存柊宸插瓨鍦ㄧ殑琛ㄧ粨鏋�
+-- =====================================================
+
+-- 妫�鏌ュ苟淇敼 tb_vehicle_alert_config 琛ㄧ粨鏋�
+-- 濡傛灉琛ㄥ凡瀛樺湪浣嗗瓧娈典笉姝g‘锛岄渶瑕佸厛鍒犻櫎鏃у瓧娈碉紝鍐嶆坊鍔犳柊瀛楁
+
+-- 1. 鍒犻櫎鏃х殑鍞竴绱㈠紩
+ALTER TABLE `tb_vehicle_alert_config` DROP INDEX IF EXISTS `uk_type_target`;
+
+-- 2. 妫�鏌ユ槸鍚﹀瓨鍦� target_id 瀛楁锛屽鏋滃瓨鍦ㄥ垯鍒犻櫎
+SET @exist_target_id := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS 
+    WHERE TABLE_SCHEMA = DATABASE() 
+    AND TABLE_NAME = 'tb_vehicle_alert_config' 
+    AND COLUMN_NAME = 'target_id');
+
+SET @sql_drop_target_id = IF(@exist_target_id > 0, 
+    'ALTER TABLE `tb_vehicle_alert_config` DROP COLUMN `target_id`', 
+    'SELECT ''target_id column does not exist''');
+PREPARE stmt FROM @sql_drop_target_id;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+-- 3. 娣诲姞 dept_id 瀛楁锛堝鏋滀笉瀛樺湪锛�
+SET @exist_dept_id := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS 
+    WHERE TABLE_SCHEMA = DATABASE() 
+    AND TABLE_NAME = 'tb_vehicle_alert_config' 
+    AND COLUMN_NAME = 'dept_id');
+
+SET @sql_add_dept_id = IF(@exist_dept_id = 0, 
+    'ALTER TABLE `tb_vehicle_alert_config` ADD COLUMN `dept_id` bigint(20) DEFAULT NULL COMMENT ''閮ㄩ棬ID锛堥儴闂ㄩ厤缃椂浣跨敤锛�'' AFTER `config_type`', 
+    'SELECT ''dept_id column already exists''');
+PREPARE stmt FROM @sql_add_dept_id;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+-- 4. 娣诲姞 vehicle_id 瀛楁锛堝鏋滀笉瀛樺湪锛�
+SET @exist_vehicle_id := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS 
+    WHERE TABLE_SCHEMA = DATABASE() 
+    AND TABLE_NAME = 'tb_vehicle_alert_config' 
+    AND COLUMN_NAME = 'vehicle_id');
+
+SET @sql_add_vehicle_id = IF(@exist_vehicle_id = 0, 
+    'ALTER TABLE `tb_vehicle_alert_config` ADD COLUMN `vehicle_id` bigint(20) DEFAULT NULL COMMENT ''杞﹁締ID锛堣溅杈嗛厤缃椂浣跨敤锛�'' AFTER `dept_id`', 
+    'SELECT ''vehicle_id column already exists''');
+PREPARE stmt FROM @sql_add_vehicle_id;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+-- 5. 妫�鏌ユ槸鍚﹀瓨鍦� notify_roles 瀛楁锛屽鏋滃瓨鍦ㄥ垯鍒犻櫎
+SET @exist_notify_roles := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS 
+    WHERE TABLE_SCHEMA = DATABASE() 
+    AND TABLE_NAME = 'tb_vehicle_alert_config' 
+    AND COLUMN_NAME = 'notify_roles');
+
+SET @sql_drop_notify_roles = IF(@exist_notify_roles > 0, 
+    'ALTER TABLE `tb_vehicle_alert_config` DROP COLUMN `notify_roles`', 
+    'SELECT ''notify_roles column does not exist''');
+PREPARE stmt FROM @sql_drop_notify_roles;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+-- 6. 淇敼 enabled 瀛楁涓� status锛堝鏋滃瓨鍦� enabled锛�
+SET @exist_enabled := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS 
+    WHERE TABLE_SCHEMA = DATABASE() 
+    AND TABLE_NAME = 'tb_vehicle_alert_config' 
+    AND COLUMN_NAME = 'enabled');
+
+SET @sql_change_enabled = IF(@exist_enabled > 0, 
+    'ALTER TABLE `tb_vehicle_alert_config` CHANGE COLUMN `enabled` `status` char(1) DEFAULT ''0'' COMMENT ''鐘舵�侊紙0-鍚敤 1-鍋滅敤锛�''', 
+    'SELECT ''enabled column does not exist, may already be status''');
+PREPARE stmt FROM @sql_change_enabled;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+-- 7. 鍒犻櫎鏃х储寮�
+ALTER TABLE `tb_vehicle_alert_config` DROP INDEX IF EXISTS `idx_target_id`;
+ALTER TABLE `tb_vehicle_alert_config` DROP INDEX IF EXISTS `idx_enabled`;
+
+-- 8. 鍒涘缓鏂扮殑鍞竴绱㈠紩
+-- 鍏堟鏌ョ储寮曟槸鍚﹀瓨鍦�
+SET @exist_uk_vehicle := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS 
+    WHERE TABLE_SCHEMA = DATABASE() 
+    AND TABLE_NAME = 'tb_vehicle_alert_config' 
+    AND INDEX_NAME = 'uk_vehicle_config');
+
+SET @sql_add_uk_vehicle = IF(@exist_uk_vehicle = 0, 
+    'ALTER TABLE `tb_vehicle_alert_config` ADD UNIQUE KEY `uk_vehicle_config` (`config_type`, `vehicle_id`)', 
+    'SELECT ''uk_vehicle_config index already exists''');
+PREPARE stmt FROM @sql_add_uk_vehicle;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @exist_uk_dept := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS 
+    WHERE TABLE_SCHEMA = DATABASE() 
+    AND TABLE_NAME = 'tb_vehicle_alert_config' 
+    AND INDEX_NAME = 'uk_dept_config');
+
+SET @sql_add_uk_dept = IF(@exist_uk_dept = 0, 
+    'ALTER TABLE `tb_vehicle_alert_config` ADD UNIQUE KEY `uk_dept_config` (`config_type`, `dept_id`)', 
+    'SELECT ''uk_dept_config index already exists''');
+PREPARE stmt FROM @sql_add_uk_dept;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+-- 9. 鍒涘缓鏂扮殑鏅�氱储寮�
+SET @exist_idx_dept := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS 
+    WHERE TABLE_SCHEMA = DATABASE() 
+    AND TABLE_NAME = 'tb_vehicle_alert_config' 
+    AND INDEX_NAME = 'idx_dept_id');
+
+SET @sql_add_idx_dept = IF(@exist_idx_dept = 0, 
+    'ALTER TABLE `tb_vehicle_alert_config` ADD KEY `idx_dept_id` (`dept_id`)', 
+    'SELECT ''idx_dept_id index already exists''');
+PREPARE stmt FROM @sql_add_idx_dept;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @exist_idx_vehicle := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS 
+    WHERE TABLE_SCHEMA = DATABASE() 
+    AND TABLE_NAME = 'tb_vehicle_alert_config' 
+    AND INDEX_NAME = 'idx_vehicle_id');
+
+SET @sql_add_idx_vehicle = IF(@exist_idx_vehicle = 0, 
+    'ALTER TABLE `tb_vehicle_alert_config` ADD KEY `idx_vehicle_id` (`vehicle_id`)', 
+    'SELECT ''idx_vehicle_id index already exists''');
+PREPARE stmt FROM @sql_add_idx_vehicle;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @exist_idx_status := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS 
+    WHERE TABLE_SCHEMA = DATABASE() 
+    AND TABLE_NAME = 'tb_vehicle_alert_config' 
+    AND INDEX_NAME = 'idx_status');
+
+SET @sql_add_idx_status = IF(@exist_idx_status = 0, 
+    'ALTER TABLE `tb_vehicle_alert_config` ADD KEY `idx_status` (`status`)', 
+    'SELECT ''idx_status index already exists''');
+PREPARE stmt FROM @sql_add_idx_status;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+-- 10. 鏇存柊宸叉湁鐨勫叏灞�閰嶇疆璁板綍
+UPDATE `tb_vehicle_alert_config` 
+SET `dept_id` = NULL, `vehicle_id` = NULL 
+WHERE `config_type` = 'GLOBAL';
+
+-- 鍗囩骇瀹屾垚鎻愮ず
+SELECT '鏁版嵁搴撳崌绾у畬鎴愶紒tb_vehicle_alert_config 琛ㄧ粨鏋勫凡鏇存柊' AS 'Status';
diff --git a/sql/vehicle_sync_menu.sql b/sql/vehicle_sync_menu.sql
new file mode 100644
index 0000000..63d49ec
--- /dev/null
+++ b/sql/vehicle_sync_menu.sql
@@ -0,0 +1,35 @@
+-- 杞﹁締鍚屾绠$悊鑿滃崟 SQL
+-- 鑿滃崟 ID鑷锛屽彲浠ユ牴鎹綘鐨勬暟鎹簱瀹為檯鎯呭喌璋冩暣
+
+-- 鐖惰彍鍗曪紙杞﹁締绠$悊锛塈D锛屽亣璁句负2020锛岄渶瑕佹牴鎹疄闄呮儏鍐佃皟鏁�
+SET @parentMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = '杞﹁締绠$悊' AND menu_type = 'M' LIMIT 1);
+
+-- 濡傛灉娌℃湁杞﹁締绠$悊鐖惰彍鍗曪紝鍏堝垱寤�
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '杞﹁締绠$悊', 0, 4, 'vehicle', NULL, 1, 0, 'M', '0', '0', '', 'car', 'admin', sysdate(), '', NULL, '杞﹁締绠$悊鐩綍'
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_name = '杞﹁締绠$悊' AND menu_type = 'M');
+
+-- 閲嶆柊鑾峰彇鐖惰彍鍗旾D
+SET @parentMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = '杞﹁締绠$悊' AND menu_type = 'M' LIMIT 1);
+
+-- 杞﹁締鍚屾绠$悊鑿滃崟
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '杞﹁締鍚屾', @parentMenuId, 5, 'vehicleSync', 'system/vehicleSync/index', 1, 0, 'C', '0', '0', 'system:vehicleSync:list', 'upload', 'admin', sysdate(), '', NULL, '杞﹁締鍚屾鑿滃崟'
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleSync:list');
+
+-- 鑾峰彇杞﹁締鍚屾鑿滃崟ID
+SET @vehicleSyncMenuId = (SELECT menu_id FROM sys_menu WHERE perms = 'system:vehicleSync:list' LIMIT 1);
+
+-- 杞﹁締鍚屾鎸夐挳
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '鍚屾杞﹁締', @vehicleSyncMenuId, 1, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleSync:sync', '#', 'admin', sysdate(), '', NULL, ''
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleSync:sync');
+
+-- 杞﹁締鍚屾鏌ヨ鎸夐挳
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT '鏌ヨ', @vehicleSyncMenuId, 2, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleSync:query', '#', 'admin', sysdate(), '', NULL, ''
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleSync:query');
diff --git "a/\345\214\273\351\231\242\344\277\241\346\201\257\345\210\206\350\257\215\346\220\234\347\264\242\345\212\237\350\203\275\350\257\264\346\230\216.md" "b/\345\214\273\351\231\242\344\277\241\346\201\257\345\210\206\350\257\215\346\220\234\347\264\242\345\212\237\350\203\275\350\257\264\346\230\216.md"
new file mode 100644
index 0000000..22154cf
--- /dev/null
+++ "b/\345\214\273\351\231\242\344\277\241\346\201\257\345\210\206\350\257\215\346\220\234\347\264\242\345\212\237\350\203\275\350\257\264\346\230\216.md"
@@ -0,0 +1,274 @@
+# 鍖婚櫌淇℃伅鍒嗚瘝鎼滅储鍔熻兘璇存槑
+
+## 鍔熻兘姒傝堪
+
+鏈姛鑳藉疄鐜颁簡鍩轰簬涓枃鍒嗚瘝鐨勫尰闄俊鎭櫤鑳芥悳绱�,閫氳繃瀵瑰尰闄㈠悕绉般�佸湴鍧�绛変俊鎭繘琛屽垎璇嶅鐞�,鏀寔妯$硦鍖归厤鍜屾潈閲嶆帓搴�,澶у箙鎻愬崌鍖婚櫌鎼滅储鐨勫噯纭�у拰鐢ㄦ埛浣撻獙銆�
+
+## 瀹炵幇鍐呭
+
+### 1. 鏁版嵁搴撳眰闈�
+
+#### 1.1 鏂板鍒嗚瘝瀛楁
+- **鏂囦欢**: `sql/tb_hosp_data_add_keywords.sql`
+- **鍐呭**: 鍦� `tb_hosp_data` 琛ㄤ腑娣诲姞 `hosp_keywords` 瀛楁
+  ```sql
+  ALTER TABLE `tb_hosp_data` 
+  ADD COLUMN `hosp_keywords` varchar(500) DEFAULT NULL COMMENT '鍖婚櫌淇℃伅鍒嗚瘝锛堥�楀彿鍒嗛殧锛�';
+  
+  CREATE INDEX idx_hosp_keywords ON tb_hosp_data(hosp_keywords);
+  ```
+
+### 2. 瀹炰綋绫诲眰闈�
+
+#### 2.1 TbHospData 瀹炰綋鎵╁睍
+- **鏂囦欢**: `ruoyi-system/src/main/java/com/ruoyi/system/domain/TbHospData.java`
+- **鏂板瀛楁**: 
+  - `hospKeywords` - 鍖婚櫌淇℃伅鍒嗚瘝锛堥�楀彿鍒嗛殧锛�
+- **鍖呭惈鏂规硶**: getter銆乻etter 鍙� toString
+
+### 3. 鍒嗚瘝宸ュ叿绫�
+
+#### 3.1 HospitalTokenizerUtil
+- **鏂囦欢**: `ruoyi-common/src/main/java/com/ruoyi/common/utils/HospitalTokenizerUtil.java`
+- **鏍稿績鍔熻兘**:
+  1. **涓枃鍒嗚瘝**: 浣跨敤 N-Gram 绠楁硶瀵瑰尰闄俊鎭繘琛屽垎璇嶏紙2-4瀛楃缁勫悎锛�
+  2. **鍋滅敤璇嶈繃婊�**: 鑷姩杩囨护"鍖婚櫌"銆�"甯�"銆�"鐪�"绛夊父瑙佸仠鐢ㄨ瘝
+  3. **鍏抽敭璇嶆彁鍙�**: 浠庡尰闄㈠悕绉般�佺畝绉般�佺渷甯傚尯銆佸湴鍧�涓彁鍙栧叧閿瘝
+  4. **鍖归厤搴﹁绠�**: 璁$畻涓や釜鍒嗚瘝闆嗗悎鐨勫尮閰嶅垎鏁�
+
+- **涓昏鏂规硶**:
+  ```java
+  // 鐢熸垚鍖婚櫌淇℃伅鐨勫垎璇�
+  public static String tokenize(String hospName, String hospShort, 
+                                 String province, String city, 
+                                 String area, String address)
+  
+  // 瀵规悳绱㈡枃鏈繘琛屽垎璇�
+  public static String tokenizeSearchText(String text)
+  
+  // 璁$畻涓や釜鍒嗚瘝闆嗗悎鐨勫尮閰嶅害
+  public static int calculateMatchScore(String keywords1, String keywords2)
+  ```
+
+### 4. Service 灞�
+
+#### 4.1 ITbHospDataService 鎺ュ彛鎵╁睍
+- **鏂囦欢**: `ruoyi-system/src/main/java/com/ruoyi/system/service/ITbHospDataService.java`
+- **鏂板鏂规硶**:
+  ```java
+  // 鎵归噺鐢熸垚骞舵洿鏂版墍鏈夊尰闄㈢殑鍒嗚瘝
+  int generateAllHospitalKeywords();
+  
+  // 涓哄崟涓尰闄㈢敓鎴愬垎璇�
+  String generateKeywordsForHospital(TbHospData tbHospData);
+  ```
+
+#### 4.2 TbHospDataServiceImpl 瀹炵幇
+- **鏂囦欢**: `ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbHospDataServiceImpl.java`
+- **鍔熻兘**:
+  - 鎵归噺澶勭悊鎵�鏈夊尰闄㈡暟鎹�,鐢熸垚鍒嗚瘝骞舵洿鏂版暟鎹簱
+  - 鍗曚釜鍖婚櫌鍒嗚瘝鐢熸垚,鍦ㄦ柊澧�/鏇存柊鍖婚櫌鏃惰嚜鍔ㄨ皟鐢�
+
+#### 4.3 HospDataSyncServiceImpl 闆嗘垚
+- **鏂囦欢**: `ruoyi-system/src/main/java/com/ruoyi/system/service/impl/HospDataSyncServiceImpl.java`
+- **鍔熻兘**: 鍦ㄥ尰闄㈠悓姝ユ椂鑷姩鐢熸垚鍒嗚瘝
+  - 浠� SQL Server 鍚屾鍖婚櫌鏁版嵁鏃�,鑷姩璋冪敤鍒嗚瘝鐢熸垚
+  - 纭繚鎵�鏈夊尰闄㈡暟鎹兘鏈夋渶鏂扮殑鍒嗚瘝淇℃伅
+
+### 5. Controller 灞�
+
+#### 5.1 HospDataController 鎵╁睍
+- **鏂囦欢**: `ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/HospDataController.java`
+
+##### 5.1.1 鎵归噺鐢熸垚鍒嗚瘝鎺ュ彛
+- **鎺ュ彛**: `GET /system/hospital/generateKeywords`
+- **鍔熻兘**: 鎵归噺鐢熸垚鎵�鏈夊尰闄㈢殑鍒嗚瘝锛堢鐞嗗憳鎺ュ彛锛�
+- **杩斿洖**: 鏇存柊鐨勫尰闄㈡暟閲�
+- **浣跨敤鍦烘櫙**: 
+  - 棣栨閮ㄧ讲鏃跺垵濮嬪寲鎵�鏈夊尰闄㈠垎璇�
+  - 鍒嗚瘝绠楁硶鍗囩骇鍚庨噸鏂扮敓鎴愬垎璇�
+
+##### 5.1.2 鍩轰簬鍒嗚瘝鍖归厤鎼滅储鎺ュ彛
+- **鎺ュ彛**: `GET /system/hospital/searchByKeywords`
+- **鍙傛暟**:
+  - `searchText` (蹇呭~): 鎼滅储鏂囨湰锛堝尰闄㈠悕绉般�佸湴鍧�绛夛級
+  - `pageSize` (鍙��): 杩斿洖缁撴灉鏁伴噺,榛樿 50
+- **鍔熻兘娴佺▼**:
+  1. 瀵瑰墠绔紶鍏ョ殑鎼滅储鏂囨湰杩涜鍒嗚瘝
+  2. 鏌ヨ鎵�鏈夋甯哥姸鎬佺殑鍖婚櫌鏁版嵁
+  3. 璁$畻姣忎釜鍖婚櫌涓庢悳绱㈡枃鏈殑鍖归厤鍒嗘暟
+  4. 鎸夊尮閰嶅垎鏁伴檷搴忔帓搴忥紙鏉冮噸鎺掑簭锛�
+  5. 杩斿洖鍖归厤鐨勫尰闄㈠垪琛�
+
+- **杩斿洖绀轰緥**:
+  ```json
+  {
+    "code": 200,
+    "msg": "鏌ヨ鎴愬姛",
+    "data": [
+      {
+        "hospId": 1001,
+        "hospName": "鍖椾含鍗忓拰鍖婚櫌",
+        "hospShort": "鍗忓拰鍖婚櫌",
+        "hopsProvince": "鍖椾含甯�",
+        "hopsCity": "鍖椾含甯�",
+        "hopsArea": "涓滃煄鍖�",
+        "hospAddress": "涓滃煄鍖哄竻搴滃洯1鍙�"
+      },
+      // ... 鏇村鍖婚櫌鏁版嵁锛堟寜鍖归厤搴︽帓搴忥級
+    ]
+  }
+  ```
+
+### 6. Mapper XML 鏇存柊
+
+#### 6.1 TbHospDataMapper.xml
+- **鏂囦欢**: `ruoyi-system/src/main/resources/mapper/system/TbHospDataMapper.xml`
+- **鏇存柊鍐呭**:
+  - ResultMap 娣诲姞 `hospKeywords` 瀛楁鏄犲皠
+  - SELECT 璇彞鍖呭惈 `hosp_keywords` 瀛楁
+  - INSERT 鍜� UPDATE 鏀寔 `hosp_keywords` 瀛楁
+
+## 浣跨敤璇存槑
+
+### 閮ㄧ讲姝ラ
+
+1. **鎵ц鏁版嵁搴撹剼鏈�**
+   ```bash
+   mysql -u鐢ㄦ埛鍚� -p鏁版嵁搴撳悕 < sql/tb_hosp_data_add_keywords.sql
+   ```
+
+2. **閲嶅惎搴旂敤**
+   ```bash
+   ./ry.sh restart
+   # 鎴� Windows 涓�
+   ry.bat
+   ```
+
+3. **鍒濆鍖栧垎璇嶆暟鎹�**
+   璋冪敤鎵归噺鐢熸垚鍒嗚瘝鎺ュ彛:
+   ```bash
+   GET http://localhost:8080/system/hospital/generateKeywords
+   ```
+
+### 鍓嶇闆嗘垚绀轰緥
+
+#### 浣跨敤鍒嗚瘝鎼滅储鎺ュ彛
+```javascript
+// 鍦� uni-app 涓皟鐢�
+uni.request({
+  url: '/system/hospital/searchByKeywords',
+  method: 'GET',
+  data: {
+    searchText: '鍖椾含鍗忓拰涓滃煄鍖�',
+    pageSize: 20
+  },
+  success: (res) => {
+    if (res.data.code === 200) {
+      // 鍖婚櫌鍒楄〃宸叉寜鍖归厤搴︽帓搴�
+      const hospitals = res.data.data;
+      console.log('鎵惧埌鍖婚櫌:', hospitals.length);
+    }
+  }
+});
+```
+
+#### Vue.js 绀轰緥
+```javascript
+// 鍦� Vue 缁勪欢涓�
+methods: {
+  async searchHospitals(searchText) {
+    try {
+      const response = await this.$http.get('/system/hospital/searchByKeywords', {
+        params: {
+          searchText: searchText,
+          pageSize: 50
+        }
+      });
+      
+      if (response.data.code === 200) {
+        this.hospitals = response.data.data;
+        // 鏁版嵁宸叉寜鍖归厤搴︽帓搴�,鍖归厤瓒婂ソ鐨勫尰闄㈣秺闈犲墠
+      }
+    } catch (error) {
+      console.error('鎼滅储澶辫触:', error);
+    }
+  }
+}
+```
+
+## 鍔熻兘鐗圭偣
+
+### 1. 鏅鸿兘鍒嗚瘝
+- 浣跨敤 N-Gram 绠楁硶鐢熸垚 2-4 瀛楃鐨勫叧閿瘝缁勫悎
+- 鑷姩鎻愬彇鍗曚釜涓枃瀛楃浣滀负琛ュ厖鍏抽敭璇�
+- 鏀寔瀵瑰尰闄㈠悕绉般�佺畝绉般�佺渷甯傚尯銆佸湴鍧�绛夊涓瓧娈靛垎璇�
+
+### 2. 鍋滅敤璇嶈繃婊�
+鑷姩杩囨护浠ヤ笅甯歌鍋滅敤璇�:
+- 閫氱敤璇�: "鍖婚櫌"銆�"璇婃墍"銆�"鍗敓"銆�"闄�"
+- 鍦板尯璇�: "甯�"銆�"鐪�"銆�"鍘�"銆�"鍖�"銆�"闀�"銆�"涔�"
+- 浣嶇疆璇�: "琛楅亾"銆�"璺�"銆�"鍙�"銆�"鏍�"銆�"鍗曞厓"銆�"瀹�"銆�"灞�"銆�"妤�"
+- 杩炴帴璇�: "鐨�"銆�"浜�"銆�"鍦�"銆�"涓�"銆�"鍜�"銆�"鍙�"绛�
+
+### 3. 鏉冮噸鎺掑簭
+- 鏍规嵁鍖归厤鐨勫叧閿瘝鏁伴噺璁$畻鍖归厤鍒嗘暟
+- 鍖归厤鐨勫垎璇嶈秺澶�,鎺掑悕瓒婇潬鍓�
+- 绮剧‘鍖归厤浼樺厛浜庢ā绯婂尮閰�
+
+### 4. 鑷姩鍖栭泦鎴�
+- 鍖婚櫌鍚屾鏃惰嚜鍔ㄧ敓鎴愬垎璇�
+- 鏂板鎴栨洿鏂板尰闄㈡椂鑷姩鏇存柊鍒嗚瘝
+- 鏃犻渶鎵嬪姩骞查,纭繚鏁版嵁涓�鑷存��
+
+## 鎬ц兘浼樺寲
+
+1. **绱㈠紩浼樺寲**: 鍦� `hosp_keywords` 瀛楁涓婂垱寤虹储寮�,鎻愬崌鏌ヨ鎬ц兘
+2. **鍐呭瓨璁$畻**: 鍖归厤搴﹁绠楀湪鍐呭瓨涓繘琛�,閬垮厤鏁版嵁搴撳帇鍔�
+3. **缁撴灉闄愬埗**: 鏀寔 `pageSize` 鍙傛暟闄愬埗杩斿洖鏁伴噺,鍑忓皯缃戠粶浼犺緭
+
+## 娉ㄦ剰浜嬮」
+
+1. **棣栨閮ㄧ讲**: 蹇呴』璋冪敤 `/generateKeywords` 鎺ュ彛鍒濆鍖栨墍鏈夊尰闄㈢殑鍒嗚瘝鏁版嵁
+2. **鏁版嵁鍚屾**: 鍖婚櫌鍚屾浼氳嚜鍔ㄦ洿鏂板垎璇�,鏃犻渶棰濆鎿嶄綔
+3. **鍒嗚瘝璐ㄩ噺**: 鍒嗚瘝绠楁硶鍩轰簬绠�鍗曠殑 N-Gram,瀵逛簬澶嶆潅鐨勫尰闄㈠悕绉板彲鑳介渶瑕佷紭鍖�
+4. **鍋滅敤璇嶇淮鎶�**: 鍙牴鎹疄闄呬笟鍔¢渶姹傚湪 `HospitalTokenizerUtil` 涓皟鏁村仠鐢ㄨ瘝鍒楄〃
+
+## 鍚庣画浼樺寲寤鸿
+
+1. **闆嗘垚绗笁鏂瑰垎璇�**: 
+   - 鍙�冭檻闆嗘垚 IK Analyzer銆丠anLP 绛変笓涓氫腑鏂囧垎璇嶅簱
+   - 鎻愬崌鍒嗚瘝鍑嗙‘搴﹀拰鍙洖鐜�
+
+2. **鎷奸煶鏀寔**:
+   - 澧炲姞鎷奸煶绱㈠紩,鏀寔鎷奸煶棣栧瓧姣嶆悳绱�
+   - 渚嬪: "bjxhyy" 鍙互鍖归厤"鍖椾含鍗忓拰鍖婚櫌"
+
+3. **鍚屼箟璇嶆墿灞�**:
+   - 鏀寔鍚屼箟璇嶅尮閰�
+   - 渚嬪: "浜烘皯鍖婚櫌" 鍜� "浜烘皯鍖荤枟涓績"
+
+4. **鎼滅储鍘嗗彶**:
+   - 璁板綍鐢ㄦ埛鎼滅储鍘嗗彶
+   - 鏍规嵁鍘嗗彶鏁版嵁浼樺寲鎺掑簭绠楁硶
+
+5. **鍦扮悊浣嶇疆鏉冮噸**:
+   - 缁撳悎鐢ㄦ埛浣嶇疆淇℃伅
+   - 浼樺厛杩斿洖闄勮繎鐨勫尰闄�
+
+## 鎶�鏈爤
+
+- **鍚庣妗嗘灦**: Spring Boot + MyBatis
+- **鏁版嵁搴�**: MySQL 5.7+
+- **宸ュ叿绫�**: Apache Commons Lang3
+- **鏃ュ織**: SLF4J + Logback
+
+## 鑱旂郴鏂瑰紡
+
+濡傛湁闂鎴栧缓璁�,璇疯仈绯诲紑鍙戝洟闃熴��
+
+---
+
+**鏂囨。鐗堟湰**: v1.0  
+**鏇存柊鏃ユ湡**: 2026-01-20  
+**寮�鍙戣��**: RuoYi Team
diff --git "a/\345\214\273\351\231\242\345\210\206\350\257\215\346\220\234\347\264\242-\345\277\253\351\200\237\344\275\277\347\224\250\346\214\207\345\215\227.md" "b/\345\214\273\351\231\242\345\210\206\350\257\215\346\220\234\347\264\242-\345\277\253\351\200\237\344\275\277\347\224\250\346\214\207\345\215\227.md"
new file mode 100644
index 0000000..e1776dc
--- /dev/null
+++ "b/\345\214\273\351\231\242\345\210\206\350\257\215\346\220\234\347\264\242-\345\277\253\351\200\237\344\275\277\347\224\250\346\214\207\345\215\227.md"
@@ -0,0 +1,432 @@
+# 鍖婚櫌鍒嗚瘝鎼滅储鍔熻兘 - 蹇�熶娇鐢ㄦ寚鍗�
+
+## 涓�銆侀儴缃叉楠わ紙4姝ュ畬鎴愶級
+
+### 姝ラ1: 闆嗘垚 HanLP 鍒嗚瘝搴�
+
+**璇存槑**: 宸查泦鎴� HanLP 涓撲笟涓枃鍒嗚瘝搴擄紝鏇夸唬浜嗙畝鍗曠殑 N-Gram 绠楁硶锛屽垎璇嶅噯纭害鏇撮珮銆�
+
+鍦� `ruoyi-common/pom.xml` 涓凡娣诲姞锛�
+```xml
+<!-- HanLP 涓枃鍒嗚瘝搴� -->
+<dependency>
+    <groupId>com.hankcs</groupId>
+    <artifactId>hanlp</artifactId>
+    <version>portable-1.8.4</version>
+</dependency>
+```
+
+### 姝ラ2: 鎵ц鏁版嵁搴撹剼鏈�
+```bash
+# 杩涘叆 SQL 鐩綍
+cd sql
+
+# 1. 鎵ц鑴氭湰娣诲姞鍒嗚瘝瀛楁
+mysql -uroot -p浣犵殑瀵嗙爜 浣犵殑鏁版嵁搴撳悕 < tb_hosp_data_add_keywords.sql
+
+# 2. 娣诲姞鍚庡彴鑿滃崟鏉冮檺
+mysql -uroot -p浣犵殑瀵嗙爜 浣犵殑鏁版嵁搴撳悕 < hospital_tokenizer_menu.sql
+```
+
+### 姝ラ3: 閲嶅惎搴旂敤
+```bash
+# Linux/Mac
+./ry.sh restart
+
+# Windows
+ry.bat
+```
+
+### 姝ラ4: 浣跨敤鍚庡彴娴嬭瘯鐣岄潰鍒濆鍖栧垎璇�
+
+**鏂瑰紡1: 閫氳繃鍚庡彴鐣岄潰锛堟帹鑽愶級**
+
+1. 鐧诲綍鍚庡彴绠$悊绯荤粺
+2. 杩涘叆鑿滃崟锛�**绯荤粺绠$悊 > 鍖婚櫌绠$悊 > 鍖婚櫌鍒嗚瘝娴嬭瘯**
+3. 鐐瑰嚮銆�**鎵归噺鐢熸垚鎵�鏈夊尰闄㈠垎璇�**銆嶆寜閽�
+4. 绛夊緟鐢熸垚瀹屾垚
+
+**鏂瑰紡2: 閫氳繃 API 鎺ュ彛**
+
+浣跨敤 Postman 鎴栨祻瑙堝櫒璁块棶锛�
+```
+GET http://localhost:8080/system/hospital/generateKeywords
+```
+
+绛夊緟杩斿洖缁撴灉,鏄剧ず鎴愬姛鐢熸垚鐨勫尰闄㈡暟閲忋��
+
+---
+
+## 浜屻�佸悗鍙版祴璇曠晫闈娇鐢�
+
+### 璁块棶璺緞
+**绯荤粺绠$悊 > 鍖婚櫌绠$悊 > 鍖婚櫌鍒嗚瘝娴嬭瘯**
+
+### 鍔熻兘璇存槑
+
+#### 1. 鎵归噺鍒嗚瘝绠$悊
+- **鍔熻兘**: 涓烘墍鏈夊尰闄㈡壒閲忕敓鎴愬垎璇嶆暟鎹�
+- **浣跨敤鍦烘櫙**: 
+  - 棣栨閮ㄧ讲鏃跺垵濮嬪寲
+  - 鍒嗚瘝绠楁硶鍗囩骇鍚庨噸鏂扮敓鎴�
+  - 鏁版嵁淇鍚庢洿鏂�
+- **鎿嶄綔**: 鐐瑰嚮銆屾壒閲忕敓鎴愭墍鏈夊尰闄㈠垎璇嶃�嶆寜閽�
+- **鑰楁椂**: 鏍规嵁鍖婚櫌鏁伴噺锛岄�氬父闇�瑕� 1-5 鍒嗛挓
+
+#### 2. 鍖婚櫌鍖归厤娴嬭瘯
+- **鍔熻兘**: 娴嬭瘯鍒嗚瘝鎼滅储鏁堟灉
+- **浣跨敤鏂规硶**:
+  1. 鍦ㄦ悳绱㈡杈撳叆鍖婚櫌鍚嶇О銆佸湴鍧�鎴栧叧閿瘝
+  2. 璁剧疆杩斿洖缁撴灉鏁伴噺锛堥粯璁� 30 鏉★級
+  3. 鐐瑰嚮銆屾悳绱€�嶆寜閽�
+  4. 鏌ョ湅鍖归厤缁撴灉锛堟寜鐩稿叧搴︽帓搴忥級
+
+#### 3. 娴嬭瘯绀轰緥
+```
+杈撳叆: "鍖椾含鍗忓拰鍖婚櫌"
+缁撴灉: 鏄剧ず鎵�鏈夊寘鍚�滃寳浜�濆拰鈥滃崗鍜屸�濈殑鍖婚櫌
+
+杈撳叆: "涓婃捣鐟為噾鍖婚櫌鍗㈡咕鍖�"
+缁撴灉: 涓婃捣鐟為噾鍖婚櫌鍙婂叾鍒嗛櫌鎺掑湪鏈�鍓�
+
+杈撳叆: "浜烘皯鍖婚櫌鏈濋槼鍖�"
+缁撴灉: 鏈濋槼鍖虹殑浜烘皯鍖婚櫌鐩稿叧缁撴灉
+```
+
+### 鐣岄潰鎴浘璇存槑
+
+1. **鎵归噺鍒嗚瘝鍖哄煙**
+   - 灞曠ず鎻愮ず淇℃伅
+   - 鐢熸垚鎸夐挳锛堝甫 loading 鐘舵�侊級
+   - 缁撴灉鏄剧ず锛堟垚鍔�/澶辫触锛�
+
+2. **鎼滅储娴嬭瘯鍖哄煙**
+   - 鎼滅储杈撳叆妗�
+   - 缁撴灉鏁伴噺璁剧疆
+   - 鍒嗚瘝缁撴灉灞曠ず锛堟爣绛惧舰寮忥級
+   - 鍖婚櫌鍒楄〃琛ㄦ牸
+   - 缁熻淇℃伅
+
+---
+
+## 涓夈�丄PI 鎺ュ彛浣跨敤
+
+### 鎺ュ彛1: 鎵归噺鐢熸垚鍒嗚瘝锛堢鐞嗗憳锛�
+
+**鎺ュ彛**: `GET /system/hospital/generateKeywords`
+
+**璇存槑**: 鎵归噺涓烘墍鏈夊尰闄㈢敓鎴愬垎璇�,鐢ㄤ簬鍒濆鍖栨垨閲嶆柊鐢熸垚
+
+**璇锋眰绀轰緥**:
+```bash
+curl -X GET "http://localhost:8080/system/hospital/generateKeywords"
+```
+
+**杩斿洖绀轰緥**:
+```json
+{
+  "code": 200,
+  "msg": "鎴愬姛鐢熸垚 1523 涓尰闄㈢殑鍒嗚瘝"
+}
+```
+
+---
+
+### 鎺ュ彛2: 鍒嗚瘝鎼滅储鍖婚櫌锛堟牳蹇冨姛鑳斤級
+
+**鎺ュ彛**: `GET /system/hospital/searchByKeywords`
+
+**鍙傛暟**:
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 | 绀轰緥 |
+|-----|------|------|------|------|
+| searchText | String | 鏄� | 鎼滅储鏂囨湰 | "鍖椾含鍗忓拰涓滃煄鍖�" |
+| pageSize | Integer | 鍚� | 杩斿洖鏁伴噺,榛樿50 | 20 |
+
+**璇锋眰绀轰緥**:
+```bash
+# 鎼滅储 "鍖椾含鍗忓拰鍖婚櫌"
+curl -X GET "http://localhost:8080/system/hospital/searchByKeywords?searchText=鍖椾含鍗忓拰鍖婚櫌&pageSize=20"
+
+# 鎼滅储 "涓婃捣鐟為噾"
+curl -X GET "http://localhost:8080/system/hospital/searchByKeywords?searchText=涓婃捣鐟為噾"
+```
+
+**杩斿洖绀轰緥**:
+```json
+{
+  "code": 200,
+  "msg": "鏌ヨ鎴愬姛",
+  "data": [
+    {
+      "hospId": 1001,
+      "hospName": "鍖椾含鍗忓拰鍖婚櫌",
+      "hospShort": "鍗忓拰鍖婚櫌",
+      "hopsProvince": "鍖椾含甯�",
+      "hopsCity": "鍖椾含甯�",
+      "hopsArea": "涓滃煄鍖�",
+      "hospAddress": "涓滃煄鍖哄竻搴滃洯1鍙�",
+      "hospTel": "010-69156114"
+    },
+    {
+      "hospId": 1002,
+      "hospName": "棣栭兘鍖荤澶у闄勫睘鍖椾含鍗忓拰鍖婚櫌瑗块櫌",
+      "hospShort": "鍗忓拰瑗块櫌",
+      "hopsProvince": "鍖椾含甯�",
+      "hopsCity": "鍖椾含甯�",
+      "hopsArea": "瑗垮煄鍖�",
+      "hospAddress": "瑗垮煄鍖哄ぇ鏈ㄤ粨鑳″悓41鍙�"
+    }
+    // ... 鏇村鍖归厤缁撴灉锛堟寜鍖归厤搴﹁嚜鍔ㄦ帓搴忥級
+  ]
+}
+```
+
+---
+
+## 鍥涖�佸墠绔泦鎴愮ず渚�
+
+### uni-app 闆嗘垚
+
+```javascript
+// 鍦ㄤ綘鐨勯〉闈㈡垨缁勪欢涓�
+export default {
+  data() {
+    return {
+      searchText: '',
+      hospitals: []
+    }
+  },
+  methods: {
+    // 鎼滅储鍖婚櫌
+    searchHospitals() {
+      if (!this.searchText.trim()) {
+        uni.showToast({
+          title: '璇疯緭鍏ユ悳绱㈠唴瀹�',
+          icon: 'none'
+        });
+        return;
+      }
+      
+      uni.showLoading({ title: '鎼滅储涓�...' });
+      
+      uni.request({
+        url: this.$baseUrl + '/system/hospital/searchByKeywords',
+        method: 'GET',
+        data: {
+          searchText: this.searchText,
+          pageSize: 30
+        },
+        success: (res) => {
+          uni.hideLoading();
+          
+          if (res.data.code === 200) {
+            this.hospitals = res.data.data;
+            console.log('鎵惧埌鍖婚櫌:', this.hospitals.length);
+            
+            if (this.hospitals.length === 0) {
+              uni.showToast({
+                title: '鏈壘鍒板尮閰嶇殑鍖婚櫌',
+                icon: 'none'
+              });
+            }
+          } else {
+            uni.showToast({
+              title: res.data.msg || '鎼滅储澶辫触',
+              icon: 'none'
+            });
+          }
+        },
+        fail: (err) => {
+          uni.hideLoading();
+          uni.showToast({
+            title: '缃戠粶璇锋眰澶辫触',
+            icon: 'none'
+          });
+          console.error('鎼滅储澶辫触:', err);
+        }
+      });
+    },
+    
+    // 閫夋嫨鍖婚櫌
+    selectHospital(hospital) {
+      // 灏嗛�変腑鐨勫尰闄俊鎭洖濉埌琛ㄥ崟
+      console.log('閫夋嫨浜嗗尰闄�:', hospital.hospName);
+      // 浣犵殑涓氬姟閫昏緫...
+    }
+  }
+}
+```
+
+### Vue.js + Axios 闆嗘垚
+
+```javascript
+// 鍦� Vue 缁勪欢涓�
+<template>
+  <div>
+    <el-input 
+      v-model="searchText" 
+      placeholder="杈撳叆鍖婚櫌鍚嶇О鎴栧湴鍧�"
+      @keyup.enter="searchHospitals">
+      <el-button slot="append" icon="el-icon-search" @click="searchHospitals"></el-button>
+    </el-input>
+    
+    <el-table :data="hospitals" v-loading="loading">
+      <el-table-column prop="hospName" label="鍖婚櫌鍚嶇О"></el-table-column>
+      <el-table-column prop="hopsCity" label="鍩庡競"></el-table-column>
+      <el-table-column prop="hospAddress" label="鍦板潃"></el-table-column>
+      <el-table-column label="鎿嶄綔">
+        <template slot-scope="scope">
+          <el-button size="mini" @click="selectHospital(scope.row)">閫夋嫨</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      searchText: '',
+      hospitals: [],
+      loading: false
+    }
+  },
+  methods: {
+    async searchHospitals() {
+      if (!this.searchText.trim()) {
+        this.$message.warning('璇疯緭鍏ユ悳绱㈠唴瀹�');
+        return;
+      }
+      
+      this.loading = true;
+      
+      try {
+        const response = await this.$http.get('/system/hospital/searchByKeywords', {
+          params: {
+            searchText: this.searchText,
+            pageSize: 50
+          }
+        });
+        
+        if (response.data.code === 200) {
+          this.hospitals = response.data.data;
+          
+          if (this.hospitals.length === 0) {
+            this.$message.info('鏈壘鍒板尮閰嶇殑鍖婚櫌');
+          } else {
+            this.$message.success(`鎵惧埌 ${this.hospitals.length} 涓尮閰嶇殑鍖婚櫌`);
+          }
+        } else {
+          this.$message.error(response.data.msg || '鎼滅储澶辫触');
+        }
+      } catch (error) {
+        console.error('鎼滅储澶辫触:', error);
+        this.$message.error('缃戠粶璇锋眰澶辫触');
+      } finally {
+        this.loading = false;
+      }
+    },
+    
+    selectHospital(hospital) {
+      // 澶勭悊閫夋嫨鍖婚櫌鐨勯�昏緫
+      console.log('閫夋嫨浜嗗尰闄�:', hospital);
+      this.$emit('hospital-selected', hospital);
+    }
+  }
+}
+</script>
+```
+
+---
+
+## 浜斻�佸姛鑳借鏄�
+
+### 1. 鍒嗚瘝鐗圭偣
+- **HanLP 涓撲笟鍒嗚瘝**: 浣跨敤涓氱晫閫氱敤鐨� HanLP 涓枃鍒嗚瘝搴�
+- **鏅鸿兘鍒嗚瘝**: 鑷姩灏嗗尰闄㈠悕绉般�佸湴鍧�绛夊垎瑙d负澶氫釜鍏抽敭璇�
+- **鍋滅敤璇嶈繃婊�**: 杩囨护鈥滃尰闄⑩�濄�佲�滃競鈥濄�佲�滅渷鈥濈瓑甯歌璇�
+- **闄嶇骇鏂规**: HanLP 澶辫触鏃惰嚜鍔ㄩ檷绾у埌 N-Gram 绠楁硶
+- **鏀寔妯$硦鍖归厤**: 杈撳叆閮ㄥ垎鍏抽敭璇嶅嵆鍙尮閰�
+
+### 2. 鏉冮噸鎺掑簭
+- 鍖归厤鐨勫叧閿瘝瓒婂,鎺掑悕瓒婇潬鍓�
+- 瀹屽叏鍖归厤浼樺厛浜庨儴鍒嗗尮閰�
+- 鑷姩鎸夌浉鍏冲害鎺掑簭,鏃犻渶鎵嬪姩绛涢��
+
+### 3. 浣跨敤鍦烘櫙
+- 鉁� 鐢ㄦ埛杈撳叆 "鍖椾含鍗忓拰" 鈫� 鍖归厤鎵�鏈夊甫"鍖椾含"鍜�"鍗忓拰"鐨勫尰闄�
+- 鉁� 鐢ㄦ埛杈撳叆 "涓滃煄鍖轰汉姘�" 鈫� 鍖归厤"涓滃煄鍖�"鍜�"浜烘皯"鐩稿叧鐨勫尰闄�
+- 鉁� 鐢ㄦ埛杈撳叆 "鐟為噾鍖婚櫌鍗㈡咕" 鈫� 鍖归厤涓婃捣鐟為噾鍖婚櫌鍙婂叾鍒嗛櫌
+
+---
+
+## 鍏�佸父瑙侀棶棰�
+
+### Q1: 棣栨閮ㄧ讲鍚庢悳绱笉鍒颁换浣曠粨鏋�?
+**A**: 闇�瑕佸厛璋冪敤 `/generateKeywords` 鎺ュ彛鍒濆鍖栧垎璇嶆暟鎹��
+
+### Q2: 鏂板鎴栦慨鏀瑰尰闄㈠悗闇�瑕侀噸鏂扮敓鎴愬垎璇嶅悧?
+**A**: 涓嶉渶瑕併�傜郴缁熶細鑷姩鍦ㄦ柊澧�/淇敼/鍚屾鍖婚櫌鏃剁敓鎴愬垎璇嶃��
+
+### Q3: 鎼滅储閫熷害鎱㈡�庝箞鍔�?
+**A**: 
+1. 纭宸茬粡鍦� `hosp_keywords` 瀛楁涓婂垱寤虹储寮�
+2. 閫傚綋鍑忓皬 `pageSize` 鍙傛暟鍊�
+3. 鑰冭檻澧炲姞鏈嶅姟鍣ㄥ唴瀛�
+
+### Q4: 鍚庡彴鑿滃崟鐪嬩笉鍒扳�滃尰闄㈢鐞嗏��?
+**A**: 
+1. 纭鎵ц浜� `hospital_tokenizer_menu.sql` 鑴氭湰
+2. 鍦ㄢ�滅郴缁熺鐞� > 瑙掕壊绠$悊鈥濅腑锛岀粰褰撳墠瑙掕壊鍒嗛厤鑿滃崟鏉冮檺
+3. 閲嶆柊鐧诲綍鍚庡彴绯荤粺
+
+### Q5: HanLP 鍒嗚瘝搴撲笅杞芥參鎬庝箞鍔�?
+**A**: 
+1. 浣跨敤闃块噷浜戞垨鑵捐浜� Maven 闀滃儚
+2. 鎴栬�呮墜鍔ㄤ笅杞� HanLP jar 鍖呮斁鍒版湰鍦� Maven 浠撳簱
+
+### Q6: 濡備綍浼樺寲鎼滅储鍑嗙‘搴�?
+**A**: 
+1. 璋冩暣 `HospitalTokenizerUtil` 涓殑鍋滅敤璇嶅垪琛�
+2. 鏍规嵁涓氬姟闇�姹傛坊鍔犲尰闄㈠埆鍚嶆槧灏�
+3. 浣跨敤鍚庡彴娴嬭瘯鐣岄潰鍙嶅娴嬭瘯鍜岃皟浼�
+
+---
+
+## 涓冦�佺淮鎶ゅ缓璁�
+
+### 瀹氭湡缁存姢
+```bash
+# 寤鸿姣忔湀閲嶆柊鐢熸垚涓�娆″垎璇嶏紙鍙�夛級
+GET /system/hospital/generateKeywords
+```
+
+### 鐩戞帶鏃ュ織
+鏌ョ湅搴旂敤鏃ュ織涓殑鍒嗚瘝鐢熸垚淇℃伅:
+```bash
+tail -f logs/sys-info.log | grep "鍖婚櫌鍒嗚瘝"
+```
+
+### 鏁版嵁澶囦唤
+```bash
+# 瀹氭湡澶囦唤鍖婚櫌鏁版嵁
+mysqldump -u鐢ㄦ埛鍚� -p鏁版嵁搴撳悕 tb_hosp_data > hosp_data_backup.sql
+```
+
+---
+
+## 涓冦�佹妧鏈敮鎸�
+
+濡傞亣鍒伴棶棰�,璇锋鏌�:
+1. 鏁版嵁搴撳瓧娈垫槸鍚︽纭坊鍔�
+2. 搴旂敤鏄惁姝e父閲嶅惎
+3. 鍒嗚瘝鏁版嵁鏄惁宸插垵濮嬪寲
+4. 鎺ュ彛鏉冮檺鏄惁閰嶇疆姝g‘
+
+璇︾粏鎶�鏈枃妗h鍙傝��: `鍖婚櫌淇℃伅鍒嗚瘝鎼滅储鍔熻兘璇存槑.md`
+
+---
+
+**鐗堟湰**: v1.0  
+**鏇存柊鏃ユ湡**: 2026-01-20

--
Gitblit v1.9.1