From 09faa36132c8cbada5327649875534ef01c1a3b1 Mon Sep 17 00:00:00 2001
From: wlzboy <66905212@qq.com>
Date: 星期四, 11 十二月 2025 20:44:31 +0800
Subject: [PATCH] feat: 优化任务里程统计

---
 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskAssigneeService.java                  |   84 
 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskAttachmentService.java                |   33 
 sql/sys_notify_task.sql                                                                           |   95 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskAssigneeServiceImpl.java          |  260 +
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TiandituMapServiceImpl.java              |    9 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/NotifyChannelConfigController.java      |   77 
 ruoyi-ui/src/views/system/notify/log/index.vue                                                    |  322 ++
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacyTransferSyncServiceImpl.java       |   91 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskVehicleManagementController.java   |    4 
 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysWelfareTaskService.java                   |   46 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotifyTaskServiceImpl.java               |  166 +
 ruoyi-common/src/main/java/com/ruoyi/common/config/SmsConfig.java                                 |   81 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/NotifySendLogMapper.java                       |  110 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacyTransferSyncTask.java                      |    8 
 ruoyi-ui/src/views/system/notify/channelConfig.vue                                                |  311 +
 ruoyi-ui/src/api/system/notify/task/index.js                                                      |   27 
 ruoyi-system/src/main/java/com/ruoyi/system/controller/NotifyTaskController.java                  |   69 
 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskVehicleService.java                   |  154 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleMileageStatsTask.java                     |    2 
 ruoyi-ui/src/api/system/notify/channelConfig.js                                                   |   44 
 ruoyi-admin/src/main/resources/application.yml                                                    |   17 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/sms/SendTaskReq.java                           |   12 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleSyncTask.java                             |    4 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacyVehicleSyncTask.java                       |    2 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotifyChannelConfigServiceImpl.java      |  104 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/sms/NotifyController.java                      |   31 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTaskMapper.java                             |   13 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/NotifyTaskMapper.java                          |   98 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleMileageStatsMapper.java                 |    2 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTaskAssigneeMapper.java                     |   10 
 ruoyi-system/src/main/java/com/ruoyi/system/controller/SmsSendController.java                     |   25 
 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysEmergencyTaskService.java                 |   33 
 clean-logs.bat                                                                                    |   48 
 app/pagesTask/components/HospitalSelector.vue                                                     |   22 
 app/pagesTask/detail.vue                                                                          |   25 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SmsServiceImpl.java                      |  298 +
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/LegacyTransferSyncMapper.java                  |    4 
 ruoyi-system/src/main/resources/mapper/system/NotifyTaskMapper.xml                                |  132 
 ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/TaskCreateVO.java                           |    2 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/OaSyncTask.java                                  |   34 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacySystemSyncTask.java                        |   20 
 app/utils/subscribe.js                                                                            |   68 
 ruoyi-common/src/main/java/com/ruoyi/common/utils/SmsUtils.java                                   |  244 +
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/ServiceOrderController.java             |    2 
 run_sms_test.bat                                                                                  |    5 
 ruoyi-system/pom.xml                                                                              |   10 
 sql/sys_notify_send_log.sql                                                                       |   52 
 doc/日志优化说明.md                                                                                     |  306 +
 run_sms_service_tests.bat                                                                         |    9 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacySystemSyncServiceImpl.java         |   35 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java                                      |    4 
 ruoyi-system/src/main/java/com/ruoyi/system/controller/NotifySendLogController.java               |   69 
 ruoyi-system/src/main/java/com/ruoyi/system/service/INotifySendLogService.java                    |  109 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskAttachmentServiceImpl.java        |  227 +
 ruoyi-system/src/main/java/com/ruoyi/system/service/INotifyDispatchService.java                   |   81 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CmsVehicleSyncTask.java                          |   12 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleMileageStatsServiceImpl.java      |  270 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleGpsSegmentMileageTask.java                |   10 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/NotifyChannelConfigMapper.java                 |   80 
 sql/notify_menu.sql                                                                               |   53 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotifyDispatchServiceImpl.java           |  321 +
 sql/notify_dict.sql                                                                               |   51 
 app/pages/index.vue                                                                               |    4 
 ruoyi-system/src/main/java/com/ruoyi/system/listener/TaskMessageListener.java                     |  137 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysEmergencyTaskServiceImpl.java         |  490 +++
 ruoyi-system/src/main/resources/mapper/system/VehicleMileageStatsMapper.xml                       |   11 
 ruoyi-ui/src/views/system/mileageStats/index.vue                                                  |   69 
 ruoyi-system/src/main/java/com/ruoyi/system/domain/NotifySendLog.java                             |  210 +
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleGpsSegmentMileageServiceImpl.java |   25 
 ruoyi-admin/src/main/resources/logback.xml                                                        |  114 
 ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java  |    2 
 ruoyi-system/src/main/java/com/ruoyi/system/service/INotifyChannelConfigService.java              |   70 
 sql/sms_config.sql                                                                                |   35 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/sms/SmsController.java                         |   43 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysWelfareTaskServiceImpl.java           |  131 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotifySendLogServiceImpl.java            |  214 +
 ruoyi-ui/src/api/system/notify/log/index.js                                                       |   27 
 ruoyi-system/src/main/java/com/ruoyi/system/domain/NotifyTask.java                                |  212 +
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/GpsSyncTask.java                                 |    4 
 ruoyi-system/src/main/java/com/ruoyi/system/service/INotifyTaskService.java                       |  121 
 ruoyi-system/src/main/resources/mapper/system/SysTaskAssigneeMapper.xml                           |    4 
 ruoyi-common/src/main/java/com/ruoyi/common/utils/civilAviation/ServiceOrderUtil.java             |    2 
 ruoyi-ui/src/views/system/notify/task/index.vue                                                   |  310 +
 ruoyi-system/src/main/resources/mapper/system/VehicleGpsMapper.xml                                |   15 
 ruoyi-system/src/main/resources/mapper/system/NotifyChannelConfigMapper.xml                       |   91 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CleanVehicleGpsTask.java                         |    4 
 app/pagesTask/create-emergency.vue                                                                |    2 
 app/pages/message/index.vue                                                                       |    4 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/CmsGpsCollectServiceImpl.java            |   20 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentSyncServiceImpl.java              |    6 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskVehicleServiceImpl.java           |  475 +-
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskServiceImpl.java                  | 1438 +-------
 ruoyi-system/src/main/java/com/ruoyi/system/controller/SmsSendReq.java                            |   16 
 ruoyi-system/src/main/java/com/ruoyi/system/domain/NotifyChannelConfig.java                       |  112 
 ruoyi-system/src/main/resources/mapper/system/SysTaskMapper.xml                                   |   14 
 ruoyi-system/src/main/java/com/ruoyi/system/service/ISmsService.java                              |   64 
 ruoyi-system/src/main/resources/mapper/system/NotifySendLogMapper.xml                             |  174 +
 97 files changed, 7,731 insertions(+), 1,886 deletions(-)

diff --git a/app/pages/index.vue b/app/pages/index.vue
index c5c90fe..556f5aa 100644
--- a/app/pages/index.vue
+++ b/app/pages/index.vue
@@ -244,7 +244,7 @@
       loading: false,
 
       // 璁㈤槄鐘舵��
-      hasSubscribed: true,
+      hasSubscribed: false,
     };
   },
   computed: {
@@ -279,7 +279,7 @@
     this.hasSubscribed = subscribeManager.checkLocalSubscribeStatus();
 
     // 鑷姩璁㈤槄锛堝鏋滄湭璁㈤槄鍒欐樉绀虹‘璁ゅ脊绐楋級
-    // this.autoSubscribeOnLaunch();
+    this.autoSubscribeOnLaunch();
 
     // 鍔犺浇鐢ㄦ埛缁戝畾杞﹁締淇℃伅
     this.loadUserVehicle();
diff --git a/app/pages/message/index.vue b/app/pages/message/index.vue
index fd682d0..95e613f 100644
--- a/app/pages/message/index.vue
+++ b/app/pages/message/index.vue
@@ -47,7 +47,7 @@
         // 娑堟伅鍒楄〃
         messages: [],
         loading: false,
-        subscribed: true,
+        subscribed: false,
       }
     },
     computed: {
@@ -68,7 +68,7 @@
     onLoad() {
       this.loadMessages()
       // 鑷姩璁㈤槄锛堝鏋滄湭璁㈤槄鍒欐樉绀虹‘璁ゅ脊绐楋級
-      // this.autoSubscribeOnLaunch()
+      this.autoSubscribeOnLaunch()
     },
     onShow() {
       // 姣忔鏄剧ず椤甸潰鏃跺埛鏂版秷鎭�
diff --git a/app/pagesTask/components/HospitalSelector.vue b/app/pagesTask/components/HospitalSelector.vue
index b82dc5b..cd4fd01 100644
--- a/app/pagesTask/components/HospitalSelector.vue
+++ b/app/pagesTask/components/HospitalSelector.vue
@@ -224,21 +224,27 @@
     
     // 閫夋嫨鍖婚櫌
     selectHospital(hospital) {
-      this.selectedHospitalName = hospital.hospName
-      this.searchKeyword = hospital.hospName
+      // 娣诲姞null妫�鏌�
+      if (!hospital) {
+        console.warn('閫夋嫨鐨勫尰闄㈠璞′负绌�');
+        return;
+      }
+      
+      this.selectedHospitalName = hospital.hospName || '';
+      this.searchKeyword = hospital.hospName || '';
       
       const hospitalData = {
-        id: hospital.hospId,
-        name: hospital.hospName,
+        id: hospital.hospId || null,
+        name: hospital.hospName || '',
         address: hospital.hospName === '瀹朵腑' ? '' : this.buildFullAddress(hospital)
       }
       
-      this.showResults = false
-      this.searchResults = []
+      this.showResults = false;
+      this.searchResults = [];
       
       // 瑙﹀彂鏇存柊浜嬩欢
-      this.$emit('input', hospitalData)
-      this.$emit('change', hospitalData)
+      this.$emit('input', hospitalData);
+      this.$emit('change', hospitalData);
     },
     
 
diff --git a/app/pagesTask/create-emergency.vue b/app/pagesTask/create-emergency.vue
index e2b1d05..146f013 100644
--- a/app/pagesTask/create-emergency.vue
+++ b/app/pagesTask/create-emergency.vue
@@ -65,7 +65,7 @@
         label="鎵ц浠诲姟浜哄憳"
         :required="false"
         :auto-add-current-user="true"
-        :current-user-removable="false"
+        :current-user-removable="true"
         :branch-dept-ids="allOrganizationIds"
         @change="onStaffChange"
       />
diff --git a/app/pagesTask/detail.vue b/app/pagesTask/detail.vue
index c84acb7..0006bca 100644
--- a/app/pagesTask/detail.vue
+++ b/app/pagesTask/detail.vue
@@ -37,7 +37,7 @@
           <view 
             class="assignee-item" 
             v-for="(assignee, index) in taskDetail.assignees" 
-            :key="'assignee-' + (assignee.userId || assignee.userName || index)"
+            :key="getAssigneeKey(assignee, index)"
           >
             <view class="assignee-index">{{ index + 1 }}</view>
             <view class="assignee-info">
@@ -260,7 +260,7 @@
         <view 
           class="payment-record-item" 
           v-for="(payment, index) in paymentInfo.paidPayments" 
-          :key="'payment-' + (payment.id || index)"
+          :key="getPaymentKey(payment, index)"
         >
           <view class="payment-header">
             <view 
@@ -1398,7 +1398,26 @@
       // 鍑哄彂鍓嶄繚璇佸氨缁紙淇濈暀鍚戝悗鍏煎锛�
       async ensureReadyThenDepart() {
         this.handleDepartAction()
-      }
+      },
+
+      // 鑾峰彇鎵ц浜哄憳鐨刱ey鍊�
+      getAssigneeKey(assignee, index) {
+        // 纭繚杩斿洖鏈夋晥鐨勫瓧绗︿覆key
+        if (!assignee) return 'assignee-' + index;
+        // 浼樺厛浣跨敤userId锛屽叾娆℃槸userName锛屾渶鍚庝娇鐢╥ndex
+        const key = assignee.userId || assignee.userName || index;
+        return 'assignee-' + (key !== null && key !== undefined ? key : index);
+      },
+      
+      // 鑾峰彇鏀粯璁板綍鐨刱ey鍊�
+      getPaymentKey(payment, index) {
+        // 纭繚杩斿洖鏈夋晥鐨勫瓧绗︿覆key
+        if (!payment) return 'payment-' + index;
+        // 浼樺厛浣跨敤id锛屽叾娆′娇鐢╥ndex
+        const key = payment.id || index;
+        return 'payment-' + (key !== null && key !== undefined ? key : index);
+      },
+      
     }
   }
 </script>
diff --git a/app/utils/subscribe.js b/app/utils/subscribe.js
index 8066c8a..ab6d316 100644
--- a/app/utils/subscribe.js
+++ b/app/utils/subscribe.js
@@ -47,7 +47,7 @@
    * @returns {boolean}
    */
   checkLocalSubscribeStatus() {
-    return true;// uni.getStorageSync('hasSubscribedTaskNotify') || false
+    return uni.getStorageSync('hasSubscribedTaskNotify') || false
   }
 
   /**
@@ -70,16 +70,43 @@
           wx.getSetting({
             withSubscriptions: true,
             success: (res) => {
-              console.log('寰俊璁㈤槄鐘舵�佹煡璇㈢粨鏋滐細', res)
-              
+              // console.log('寰俊璁㈤槄鐘舵�佹煡璇㈢粨鏋滐細', res.subscriptionsSetting.mainSwitch)
+
               // 妫�鏌ubscriptionsSetting涓槸鍚︽湁璇ユā鏉縄D鐨勮褰�
               if (res.subscriptionsSetting && res.subscriptionsSetting.mainSwitch) {
-                const subscribeStatus = res.subscriptionsSetting.mainSwitch;
-                resolve(subscribeStatus)
+                resolve(false); // 濡傛灉鐢ㄦ埛鍚屾剰浜嗚闃咃紝鍒欒繑鍥瀎alse
+                // const subscribeStatus = res.subscriptionsSetting.mainSwitch;
+                // console.log(res.subscriptionsSetting.itemSettings[templateId])
+                // resolve(subscribeStatus)
                 // 'accept' 琛ㄧず鐢ㄦ埛鍚屾剰璁㈤槄锛�'reject' 琛ㄧず鎷掔粷锛�'ban' 琛ㄧず琚皝绂�
                 // const isSubscribed = subscribeStatus === 'accept'
                 // console.log(`妯℃澘ID ${templateId} 璁㈤槄鐘舵�侊細`, subscribeStatus, '鏄惁宸茶闃咃細', isSubscribed)
                 // resolve(isSubscribed)
+                // console.log("鍙戣捣璁㈤槄璇锋眰")
+                // wx.requestSubscribeMessage({
+                //   tmplIds: [templateId],
+                //   success: (res) => {
+                //     console.log('璁㈤槄娑堟伅鐘舵�侊細', res)
+                //     if (res[templateId] === 'accept') {
+                //       console.log('鐢ㄦ埛宸茶闃�')
+                //       resolve(true)
+                //     } else if (res[templateId] === 'reject') {
+                //       console.log('鐢ㄦ埛宸叉嫆缁濊闃�')
+                //       resolve(false)
+                //     } else if (res[templateId] === 'ban') {
+                //       console.log('鐢ㄦ埛宸插皝绂佽闃�')
+                //       resolve(false)
+                //     } else {
+                //       console.log('鏈煡璁㈤槄鐘舵��')
+                //       resolve(false)
+                //     }
+                //   },fail: (err) => {
+                //     console.error('璁㈤槄娑堟伅澶辫触锛�', err)
+                //     resolve(false)
+                //   }
+
+                // }
+                // )
               } else {
                 console.log('鏈壘鍒拌闃呰缃俊鎭紝瑙嗕负鏈闃�')
                 resolve(false)
@@ -112,16 +139,16 @@
   async checkSubscribeStatus() {
     const localStatus = this.checkLocalSubscribeStatus()
     const wechatStatus = await this.checkWechatSubscribeStatus()
-    
+
     // 濡傛灉鏈湴鏄剧ず宸茶闃咃紝浣嗗井淇″畼鏂规樉绀烘湭璁㈤槄锛岄渶瑕侀噸鏂拌闃�
-    const needResubscribe =  wechatStatus
-    
+    const needResubscribe = !localStatus
+
     if (needResubscribe) {
-      console.warn('鏈湴鐘舵�佷笌寰俊瀹樻柟鐘舵�佷笉涓�鑷达紝闇�瑕侀噸鏂拌闃�')
+      // console.warn('鏈湴鐘舵�佷笌寰俊瀹樻柟鐘舵�佷笉涓�鑷达紝闇�瑕侀噸鏂拌闃�')
       // 娓呴櫎鏈湴璁板綍
-      uni.removeStorageSync('hasSubscribedTaskNotify')
+      // uni.removeStorageSync('hasSubscribedTaskNotify')
     }
-    
+
     return {
       local: localStatus,
       wechat: wechatStatus,
@@ -175,7 +202,7 @@
    * @returns {Promise}
    */
   async subscribeTaskNotify(options = {}) {
-    const { 
+    const {
       showConfirm = true,
       onSuccess,
       onReject,
@@ -208,7 +235,7 @@
           success: (res) => {
             console.log('璁㈤槄娑堟伅鎺堟潈缁撴灉锛�', res)
             const templateId = this.wechatConfig.taskNotifyTemplateId
-            
+
             if (res[templateId] === 'accept') {
               // 璁板綍宸茶闃�
               uni.setStorageSync('hasSubscribedTaskNotify', true)
@@ -216,7 +243,7 @@
                 title: '璁㈤槄鎴愬姛',
                 icon: 'success'
               })
-              
+
               if (onSuccess) onSuccess()
               resolve({ success: true, action: 'accept' })
             } else if (res[templateId] === 'reject') {
@@ -224,7 +251,7 @@
                 title: '鎮ㄦ嫆缁濅簡璁㈤槄',
                 icon: 'none'
               })
-              
+
               if (onReject) onReject()
               resolve({ success: false, action: 'reject' })
             } else {
@@ -238,7 +265,7 @@
               title: '璁㈤槄澶辫触',
               icon: 'none'
             })
-            
+
             if (onFail) onFail(err)
             reject(err)
           }
@@ -284,13 +311,13 @@
    */
   async autoSubscribe(options = {}) {
     const { force = false } = options
-    
+
     try {
       // 缁煎悎妫�鏌ヨ闃呯姸鎬侊紙鏈湴 + 寰俊瀹樻柟锛�
       const status = await this.checkSubscribeStatus()
-      
+
       console.log('璁㈤槄鐘舵�佹鏌ョ粨鏋滐細', status)
-      
+
       // 濡傛灉寰俊瀹樻柟鐘舵�佹樉绀哄凡璁㈤槄锛屼笖涓嶅己鍒惰闃�
       if (status.isSubscribed && !force) {
         console.log('鐢ㄦ埛宸茶闃呰繃锛堝井淇″畼鏂圭姸鎬侊級锛岃烦杩囪嚜鍔ㄨ闃�')
@@ -302,8 +329,9 @@
         console.log('妫�娴嬪埌璁㈤槄鐘舵�佸け鏁堬紝瑙﹀彂閲嶆柊璁㈤槄娴佺▼')
       } else {
         console.log('鐢ㄦ埛鏈闃咃紝瑙﹀彂鑷姩璁㈤槄娴佺▼')
+        return { success: false, action: 'not_subscribed', skipped: true, status }
       }
-      
+
       // 鏄剧ず纭寮圭獥骞惰闃� 鐩存帴榛樿璁㈤槄
       const result = await this.subscribeWithConfirm();
       return { ...result, status }
diff --git a/clean-logs.bat b/clean-logs.bat
new file mode 100644
index 0000000..b4ce404
--- /dev/null
+++ b/clean-logs.bat
@@ -0,0 +1,48 @@
+@echo off
+REM ============================================
+REM 鏃ュ織娓呯悊鑴氭湰 - Windows鐗堟湰
+REM 鐢ㄩ��: 娓呯悊瓒呰繃鎸囧畾澶╂暟鐨勬棩蹇楁枃浠�
+REM 浣跨敤: clean-logs.bat [澶╂暟] 榛樿30澶�
+REM ============================================
+
+setlocal enabledelayedexpansion
+
+REM 璁剧疆榛樿淇濈暀澶╂暟
+set DAYS=30
+if not "%1"=="" set DAYS=%1
+
+REM 鏃ュ織鐩綍
+set LOG_DIR=logs
+
+echo ========================================
+echo 鏃ュ織娓呯悊宸ュ叿
+echo ========================================
+echo 鏃ュ織鐩綍: %LOG_DIR%
+echo 淇濈暀澶╂暟: %DAYS% 澶�
+echo ========================================
+echo.
+
+REM 妫�鏌ユ棩蹇楃洰褰曟槸鍚﹀瓨鍦�
+if not exist "%LOG_DIR%" (
+    echo [閿欒] 鏃ュ織鐩綍涓嶅瓨鍦�: %LOG_DIR%
+    goto :end
+)
+
+echo 寮�濮嬫竻鐞� %DAYS% 澶╁墠鐨勬棩蹇楁枃浠�...
+echo.
+
+REM 娓呯悊鏃ф棩蹇楁枃浠�
+forfiles /p "%LOG_DIR%" /m *.log /d -%DAYS% /c "cmd /c echo 鍒犻櫎: @path && del @path" 2>nul
+
+if %errorlevel% equ 0 (
+    echo.
+    echo [鎴愬姛] 鏃ュ織娓呯悊瀹屾垚锛�
+) else (
+    echo.
+    echo [鎻愮ず] 娌℃湁鎵惧埌闇�瑕佹竻鐞嗙殑鏃ュ織鏂囦欢
+)
+
+:end
+echo.
+pause
+
diff --git "a/doc/\346\227\245\345\277\227\344\274\230\345\214\226\350\257\264\346\230\216.md" "b/doc/\346\227\245\345\277\227\344\274\230\345\214\226\350\257\264\346\230\216.md"
new file mode 100644
index 0000000..235615f
--- /dev/null
+++ "b/doc/\346\227\245\345\277\227\344\274\230\345\214\226\350\257\264\346\230\216.md"
@@ -0,0 +1,306 @@
+# 鐢熶骇鐜鏃ュ織浼樺寲璇存槑
+
+## 馃搵 浼樺寲鍐呭鎬昏
+
+### 1. **鏃ュ織绾у埆浼樺寲**
+- 鉁� 鐢熶骇鐜鍏ㄥ眬鏃ュ織绾у埆浠� `INFO` 璋冩暣涓� `WARN`
+- 鉁� 绯荤粺妯″潡 `com.ruoyi` 浠� `INFO` 璋冩暣涓� `WARN`
+- 鉁� Spring 妗嗘灦浠� `WARN` 璋冩暣涓� `ERROR`
+- 鉁� 绗笁鏂圭粍浠讹紙Druid銆丮yBatis銆丠TTP瀹㈡埛绔級璁句负 `ERROR`
+
+### 2. **鏃ュ織鏂囦欢绠$悊浼樺寲**
+- 鉁� 鍗曚釜鏃ュ織鏂囦欢鏈�澶� **100MB**锛堣秴杩囧悗鑷姩鍒囧壊锛�
+- 鉁� INFO鏃ュ織淇濈暀 **30澶�**锛堝師60澶╋級
+- 鉁� ERROR鏃ュ織淇濈暀 **60澶�**锛堜繚鎸佷笉鍙橈級
+- 鉁� 鐢ㄦ埛鏃ュ織淇濈暀 **30澶�**锛堝師60澶╋級
+- 鉁� 鎱QL鏃ュ織淇濈暀 **15澶�**
+- 鉁� 鏃ュ織鎬诲ぇ灏忛檺鍒讹細
+  - INFO鏃ュ織: 10GB
+  - ERROR鏃ュ織: 5GB
+  - 鐢ㄦ埛鏃ュ織: 5GB
+  - 鎱QL鏃ュ織: 2GB
+
+### 3. **鏃ュ織鏂囦欢鍛藉悕**
+浼樺寲鍚庣殑鏃ュ織鏂囦欢鍛藉悕鏍煎紡锛�
+```
+sys-info.2025-12-06.0.log    # 褰撳ぉ绗�1涓枃浠�
+sys-info.2025-12-06.1.log    # 褰撳ぉ绗�2涓枃浠讹紙鍓嶄竴涓秴杩�100MB锛�
+sys-error.2025-12-06.0.log
+sys-user.2025-12-06.0.log
+slow-sql.2025-12-06.0.log    # 鏂板鎱QL鏃ュ織
+```
+
+### 4. **鐜闅旂**
+- **寮�鍙�/娴嬭瘯鐜** (`dev`, `test`)
+  - 鏃ュ織绾у埆: INFO
+  - 杈撳嚭鍒版帶鍒跺彴 + 鏂囦欢
+  - 鍖呭惈SQL璋冭瘯鏃ュ織
+  
+- **鐢熶骇鐜** (`prod`)
+  - 鏃ュ織绾у埆: WARN
+  - 浠呰緭鍑哄埌鏂囦欢锛堜笉杈撳嚭鎺у埗鍙帮紝鑺傜渷璧勬簮锛�
+  - 鎱QL鍗曠嫭璁板綍
+
+### 5. **鐗瑰畾妯″潡淇濈暀INFO鏃ュ織**
+浠ヤ笅妯″潡鍦ㄧ敓浜х幆澧冧粛淇濈暀INFO绾у埆锛堜究浜庨棶棰樻帓鏌ワ級锛�
+- `LegacySystemSyncServiceImpl` - 鏃х郴缁熷悓姝ユ湇鍔�
+- `VehicleGpsSegmentMileageServiceImpl` - GPS閲岀▼璁$畻鏈嶅姟
+
+---
+
+## 馃敡 閰嶇疆鏂囦欢璇存槑
+
+### logback.xml 鏍稿績閰嶇疆
+
+#### 鏃ュ織绾у埆璁剧疆
+```xml
+<!-- 鐢熶骇鐜 -->
+<springProfile name="prod">
+    <!-- 鍏ㄥ眬WARN绾у埆锛屽噺灏�90%鐨処NFO鏃ュ織 -->
+    <logger name="com.ruoyi" level="warn" />
+    <logger name="org.springframework" level="error" />
+    
+    <!-- 閲嶈涓氬姟妯″潡淇濈暀INFO -->
+    <logger name="com.ruoyi.system.service.impl.LegacySystemSyncServiceImpl" level="info" />
+</springProfile>
+```
+
+#### 鏃ュ織婊氬姩绛栫暐
+```xml
+<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+    <!-- 鎸夊ぉ鍒嗗壊锛屾寜澶у皬鍒嗗壊 -->
+    <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+    <maxFileSize>100MB</maxFileSize>      <!-- 鍗曟枃浠舵渶澶�100MB -->
+    <maxHistory>30</maxHistory>           <!-- 淇濈暀30澶� -->
+    <totalSizeCap>10GB</totalSizeCap>     <!-- 鎬诲ぇ灏忎笉瓒呰繃10GB -->
+</rollingPolicy>
+```
+
+---
+
+## 馃搳 浼樺寲鏁堟灉棰勪及
+
+### 鏃ュ織閲忓噺灏戝姣�
+
+| 鐜 | 浼樺寲鍓� | 浼樺寲鍚� | 鍑忓皯姣斾緥 |
+|------|--------|--------|----------|
+| **鏃ュ織绾у埆** | INFO | WARN | - |
+| **姣忔棩鏃ュ織閲�** | ~2-5GB | ~200-500MB | **80-90% 鈫�** |
+| **纾佺洏鍗犵敤** | ~120GB (60澶�) | ~15GB (30澶�) | **87.5% 鈫�** |
+| **I/O鍘嬪姏** | 楂� | 浣� | **80% 鈫�** |
+
+### 鎬ц兘鎻愬崌
+
+- 鉁� 鍑忓皯鏃ュ織鍐欏叆鐨� I/O 鎿嶄綔
+- 鉁� 闄嶄綆纾佺洏绌洪棿鍗犵敤
+- 鉁� 鍑忓皯鏃ュ織褰掓。鍘嬪姏
+- 鉁� 鎻愰珮鏃ュ織鏌ヨ鏁堢巼
+
+---
+
+## 馃搨 鏃ュ織鏂囦欢鍒嗙被
+
+### 1. sys-info.log
+- **鍐呭**: WARN绾у埆鐨勪笟鍔℃棩蹇�
+- **澶у皬**: 鍗曟枃浠�100MB
+- **淇濈暀**: 30澶╋紝鎬婚噺10GB
+- **鐢ㄩ��**: 鏃ュ父涓氬姟鐩戞帶
+
+### 2. sys-error.log
+- **鍐呭**: ERROR绾у埆鐨勯敊璇棩蹇�
+- **澶у皬**: 鍗曟枃浠�100MB
+- **淇濈暀**: 60澶╋紝鎬婚噺5GB
+- **鐢ㄩ��**: 閿欒鎺掓煡銆佹晠闅滃垎鏋�
+
+### 3. sys-user.log
+- **鍐呭**: 鐢ㄦ埛鎿嶄綔瀹¤鏃ュ織
+- **澶у皬**: 鍗曟枃浠�100MB
+- **淇濈暀**: 30澶╋紝鎬婚噺5GB
+- **鐢ㄩ��**: 鐢ㄦ埛琛屼负瀹¤
+
+### 4. slow-sql.log 猸� 鏂板
+- **鍐呭**: 鎱QL鏌ヨ鏃ュ織锛�>1000ms锛�
+- **澶у皬**: 鍗曟枃浠�50MB
+- **淇濈暀**: 15澶╋紝鎬婚噺2GB
+- **鐢ㄩ��**: 鏁版嵁搴撴�ц兘浼樺寲
+
+---
+
+## 馃洜锔� 鏃ュ織绠$悊宸ュ叿
+
+### 鎵嬪姩娓呯悊鏃ュ織
+```bash
+# Windows
+clean-logs.bat        # 娓呯悊30澶╁墠鐨勬棩蹇�
+clean-logs.bat 15     # 娓呯悊15澶╁墠鐨勬棩蹇�
+
+# Linux (濡傞渶瑕佸彲鍒涘缓)
+./clean-logs.sh
+./clean-logs.sh 15
+```
+
+### 鏌ョ湅鏃ュ織缁熻
+```bash
+# 鏌ョ湅鏃ュ織鐩綍澶у皬
+cd logs
+dir /s
+
+# 鏌ョ湅鎱QL鏃ュ織
+type slow-sql.2025-12-06.0.log
+
+# 缁熻ERROR鏁伴噺
+findstr /c:"ERROR" sys-error.2025-12-06.0.log
+```
+
+---
+
+## 馃幆 浣跨敤寤鸿
+
+### 1. 浠g爜涓殑鏃ュ織浣跨敤瑙勮寖
+
+#### 鉁� 鎺ㄨ崘鍋氭硶
+```java
+// 鐢熶骇鐜寤鸿浣跨敤WARN绾у埆璁板綍閲嶈淇℃伅
+log.warn("浠诲姟鎵ц澶辫触锛屼换鍔D: {}, 鍘熷洜: {}", taskId, reason);
+
+// ERROR绾у埆璁板綍寮傚父
+log.error("鏁版嵁搴撹繛鎺ュけ璐�", exception);
+
+// INFO绾у埆浠呯敤浜庡叧閿笟鍔℃祦绋嬶紙浼氳璁板綍锛�
+log.info("璋冨害鍗曞悓姝ュ畬鎴愶紝浠诲姟ID: {}", taskId);
+```
+
+#### 鉂� 閬垮厤鍋氭硶
+```java
+// 閬垮厤鍦ㄥ惊鐜腑浣跨敤INFO鏃ュ織锛堢敓浜х幆澧冨凡鏀逛负WARN锛屼笉浼氳褰曪級
+for (Task task : tasks) {
+    log.info("澶勭悊浠诲姟: {}", task.getId());  // 鉂� 涓嶄細璁板綍
+}
+
+// 閬垮厤璁板綍澶ч噺璋冭瘯淇℃伅
+log.info("瀹屾暣鏁版嵁: {}", JSON.toJSONString(largeObject));  // 鉂� 涓嶄細璁板綍
+```
+
+### 2. 鎬ц兘鐩戞帶鍏虫敞鐐�
+
+#### 鎱QL鏃ュ織鐩戞帶
+```bash
+# 姣忓ぉ妫�鏌ユ參SQL鏃ュ織
+type logs\slow-sql.2025-12-06.0.log
+
+# 閲嶇偣鍏虫敞锛�
+# - 鎵ц鏃堕棿瓒呰繃1绉掔殑SQL
+# - 閲嶅鍑虹幇鐨勬參SQL
+# - 琛ㄦ壂鎻忥紙鍏ㄨ〃鎵弿锛�
+```
+
+#### 閿欒鏃ュ織鐩戞帶
+```bash
+# 姣忓ぉ妫�鏌ラ敊璇棩蹇�
+type logs\sys-error.2025-12-06.0.log
+
+# 閲嶇偣鍏虫敞锛�
+# - 鏁版嵁搴撹繛鎺ュ紓甯�
+# - 绌烘寚閽堝紓甯�
+# - 涓氬姟閫昏緫閿欒
+```
+
+### 3. 鏃ュ織鍒嗘瀽
+
+#### 浣跨敤鏃ュ織鍒嗘瀽宸ュ叿
+鎺ㄨ崘浣跨敤浠ヤ笅宸ュ叿鍒嗘瀽鏃ュ織锛�
+- **Logstash + Elasticsearch + Kibana (ELK)**
+- **Splunk**
+- **Graylog**
+- **鏈湴宸ュ叿**: Notepad++, VS Code
+
+#### 甯哥敤鍒嗘瀽鍛戒护
+```bash
+# 缁熻閿欒娆℃暟
+findstr /c:"ERROR" logs\sys-error.*.log | find /c "ERROR"
+
+# 鏌ユ壘鐗瑰畾鍏抽敭璇�
+findstr /c:"slow sql" logs\sys-info.*.log
+
+# 鏌ョ湅鏈�杩戠殑閿欒
+powershell "Get-Content logs\sys-error.log -Tail 50"
+```
+
+---
+
+## 鈿狅笍 娉ㄦ剰浜嬮」
+
+### 1. 鍗囩骇鍚庨娆¤繍琛�
+- 鏃ф棩蹇楁枃浠朵笉浼氳嚜鍔ㄥ垹闄わ紝闇�鎵嬪姩娓呯悊
+- 鏂扮殑鏃ュ織鏂囦欢鍚嶅寘鍚簭鍙� `.%i`
+
+### 2. 纾佺洏绌洪棿鐩戞帶
+- 寤鸿璁剧疆纾佺洏绌洪棿鍛婅锛堜綆浜�10GB锛�
+- 瀹氭湡妫�鏌ユ棩蹇楃洰褰曞ぇ灏�
+
+### 3. 鏃ュ織绾у埆璋冩暣
+濡傛灉鐢熶骇鐜闇�瑕佷复鏃跺惎鐢―EBUG鏃ュ織鎺掓煡闂锛�
+```xml
+<!-- 涓存椂淇敼logback.xml -->
+<springProfile name="prod">
+    <!-- 涓存椂鍚敤DEBUG -->
+    <logger name="com.ruoyi.system.service.impl.SysTaskServiceImpl" level="debug" />
+</springProfile>
+```
+
+璁板緱闂鎺掓煡鍚庢敼鍥� `warn` 绾у埆锛�
+
+### 4. 鎱QL闃堝�艰皟鏁�
+鍦� `application-prod.yml` 涓彲浠ヨ皟鏁存參SQL闃堝�硷細
+```yaml
+druid:
+  filter:
+    stat:
+      slow-sql-millis: 1000  # 璋冩暣涓�500ms鎴�2000ms
+```
+
+---
+
+## 馃攧 鍥炴粴鏂规
+
+濡傛灉浼樺寲鍚庡彂鐜伴棶棰橈紝鍙互蹇�熷洖婊氾細
+
+### 鏂规1: 淇敼鏃ュ織绾у埆
+```xml
+<!-- 鍦╨ogback.xml涓慨鏀� -->
+<springProfile name="prod">
+    <logger name="com.ruoyi" level="info" />  <!-- 鏀瑰洖info -->
+</springProfile>
+```
+
+### 鏂规2: 鎭㈠鍘熼厤缃�
+淇濈暀浜嗘棫閰嶇疆鐨勫浠斤紝鍙互浠嶨it鎭㈠锛�
+```bash
+git checkout HEAD~1 -- ruoyi-admin/src/main/resources/logback.xml
+```
+
+---
+
+## 馃摓 闂鍙嶉
+
+濡傚彂鐜颁互涓嬫儏鍐碉紝璇峰強鏃惰皟鏁撮厤缃細
+- 鉂� 閲嶈鏃ュ織娌℃湁璁板綍锛堟彁鍗囩浉鍏虫ā鍧楁棩蹇楃骇鍒級
+- 鉂� 鏃ュ織閲忎粛鐒惰繃澶э紙闄嶄綆鏃ュ織绾у埆鎴栧噺灏戜繚鐣欏ぉ鏁帮級
+- 鉂� 纾佺洏绌洪棿涓嶈冻锛堝噺灏� totalSizeCap 鎴� maxHistory锛�
+- 鉁� 闇�瑕佹洿璇︾粏鐨勮皟璇曚俊鎭紙涓存椂鍚敤DEBUG绾у埆锛�
+
+---
+
+## 馃搱 鍚庣画浼樺寲寤鸿
+
+1. **寮曞叆鏃ュ織鑱氬悎绯荤粺** (ELK Stack)
+2. **瀹炴椂鏃ュ織鐩戞帶鍛婅**
+3. **鎸変笟鍔℃ā鍧楀垎绂绘棩蹇楁枃浠�**
+4. **鏃ュ織閲囨牱**锛堥珮娴侀噺鎺ュ彛锛�
+5. **寮傛鏃ュ織鍐欏叆**锛堟彁鍗囨�ц兘锛�
+
+---
+
+**鏇存柊鏃堕棿**: 2025-12-06  
+**浼樺寲鐗堟湰**: v2.0  
+**缁存姢浜�**: 寮�鍙戝洟闃�
diff --git a/run_sms_service_tests.bat b/run_sms_service_tests.bat
new file mode 100644
index 0000000..84e63ce
--- /dev/null
+++ b/run_sms_service_tests.bat
@@ -0,0 +1,9 @@
+@echo off
+echo Running SmsService tests...
+cd /d "D:\project\鎬ユ晳杞繍\code\Api\RuoYi-Vue-master"
+echo.
+echo Running all SMS service tests...
+mvn test -pl ruoyi-system -Dtest=com.ruoyi.system.service.impl.SmsServiceAllTests
+echo.
+echo All tests completed.
+pause
\ No newline at end of file
diff --git a/run_sms_test.bat b/run_sms_test.bat
new file mode 100644
index 0000000..c79cbdf
--- /dev/null
+++ b/run_sms_test.bat
@@ -0,0 +1,5 @@
+@echo off
+echo Running SMS Service Test...
+cd /d "D:\project\鎬ユ晳杞繍\code\Api\RuoYi-Vue-master"
+mvn test -pl ruoyi-system -Dtest=SmsServiceImplTest
+pause
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/sms/NotifyController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/sms/NotifyController.java
new file mode 100644
index 0000000..9520acc
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/sms/NotifyController.java
@@ -0,0 +1,31 @@
+package com.ruoyi.web.controller.sms;
+
+import com.ruoyi.common.annotation.Anonymous;
+import com.ruoyi.system.service.IWechatTaskNotifyService;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@Anonymous
+@RestController
+@RequestMapping("/system/notify")
+public class NotifyController {
+
+    @Autowired
+    private IWechatTaskNotifyService wechatTaskNotifyService;
+    @Anonymous
+    @PostMapping("/sendWeiXin")
+    public String notify(@RequestBody @Validated SendTaskReq req) {
+        List<Long> userIds = new java.util.ArrayList<>();
+        userIds.add(req.getUserId());
+        Integer result=wechatTaskNotifyService.sendTaskNotifyMessage(req.getTaskId(), userIds,null);
+        System.out.println(result);
+        return "success";
+    }
+}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/sms/SendTaskReq.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/sms/SendTaskReq.java
new file mode 100644
index 0000000..4c94294
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/sms/SendTaskReq.java
@@ -0,0 +1,12 @@
+package com.ruoyi.web.controller.sms;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class SendTaskReq implements Serializable {
+    private Long taskId;
+    private Long userId;
+
+}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/sms/SmsController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/sms/SmsController.java
new file mode 100644
index 0000000..4e39ab6
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/sms/SmsController.java
@@ -0,0 +1,43 @@
+package com.ruoyi.web.controller.sms;
+
+import com.ruoyi.common.annotation.Anonymous;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.system.controller.SmsSendController;
+import com.ruoyi.system.controller.SmsSendReq;
+import com.ruoyi.system.domain.OrderStatusCallBackVo;
+import com.ruoyi.web.controller.system.ServiceOrderController;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+
+@Anonymous
+@RestController
+@RequestMapping("/system/sms")
+public class SmsController extends BaseController {
+
+    private static final Logger log = LoggerFactory.getLogger(SmsController.class);
+
+
+    @Autowired
+    private SmsSendController smsSendController;
+
+    @Anonymous
+    @PostMapping("/send")
+    public AjaxResult sendSms(@RequestBody SmsSendReq req) {
+        try {
+           String result= smsSendController.sendSms(req);
+            return AjaxResult.success(result);
+        } catch (Exception e) {
+            return AjaxResult.error("鍙戦�佸け璐ワ細" + e.getMessage());
+        }
+    }
+
+
+}
+
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/NotifyChannelConfigController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/NotifyChannelConfigController.java
new file mode 100644
index 0000000..f661b05
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/NotifyChannelConfigController.java
@@ -0,0 +1,77 @@
+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.NotifyChannelConfig;
+import com.ruoyi.system.service.INotifyChannelConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 閫氱煡娓犻亾閰嶇疆Controller
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+@RestController
+@RequestMapping("/system/notify/channel/config")
+public class NotifyChannelConfigController extends BaseController {
+    @Autowired
+    private INotifyChannelConfigService notifyChannelConfigService;
+
+    /**
+     * 鏌ヨ閫氱煡娓犻亾閰嶇疆鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('system:notify:channel:config:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(NotifyChannelConfig notifyChannelConfig) {
+        startPage();
+        List<NotifyChannelConfig> list = notifyChannelConfigService.selectNotifyChannelConfigList(notifyChannelConfig);
+        return getDataTable(list);
+    }
+
+    /**
+     * 鑾峰彇閫氱煡娓犻亾閰嶇疆璇︾粏淇℃伅
+     */
+    @PreAuthorize("@ss.hasPermi('system:notify:channel:config:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
+        return AjaxResult.success(notifyChannelConfigService.selectNotifyChannelConfigById(id));
+    }
+
+    /**
+     * 鏂板閫氱煡娓犻亾閰嶇疆
+     */
+    @PreAuthorize("@ss.hasPermi('system:notify:channel:config:add')")
+    @Log(title = "閫氱煡娓犻亾閰嶇疆", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody NotifyChannelConfig notifyChannelConfig) {
+        return toAjax(notifyChannelConfigService.insertNotifyChannelConfig(notifyChannelConfig));
+    }
+
+    /**
+     * 淇敼閫氱煡娓犻亾閰嶇疆
+     */
+    @PreAuthorize("@ss.hasPermi('system:notify:channel:config:edit')")
+    @Log(title = "閫氱煡娓犻亾閰嶇疆", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody NotifyChannelConfig notifyChannelConfig) {
+        return toAjax(notifyChannelConfigService.updateNotifyChannelConfig(notifyChannelConfig));
+    }
+
+    /**
+     * 鍒犻櫎閫氱煡娓犻亾閰嶇疆
+     */
+    @PreAuthorize("@ss.hasPermi('system:notify:channel:config:remove')")
+    @Log(title = "閫氱煡娓犻亾閰嶇疆", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids) {
+        return toAjax(notifyChannelConfigService.deleteNotifyChannelConfigByIds(ids));
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/ServiceOrderController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/ServiceOrderController.java
index 4205f96..421a54a 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/ServiceOrderController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/ServiceOrderController.java
@@ -287,7 +287,7 @@
         //鐭ユ儏鍚屾剰涔�
         List<String> imageUrls = dispatchOrdService.selectImageUrlsByDOrdIDDt(orderDetail.getServiceOrdID());  //姝e紡浣跨敤
         otherInfo.put("imageUrls", imageUrls);
-        log.info("銆愮洃娴嬨�戠煡鎯呭悓鎰忎功:{}", imageUrls);
+//        log.info("銆愮洃娴嬨�戠煡鎯呭悓鎰忎功:{}", imageUrls);
 
         //鍙戠エ鑾峰彇
         Map<String, Object> invoiceInfo = new HashMap<>();
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskVehicleManagementController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskVehicleManagementController.java
index cc7dd4d..0d9b76a 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskVehicleManagementController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskVehicleManagementController.java
@@ -142,7 +142,7 @@
     @PostMapping("/assign")
     public AjaxResult assignVehicle(@RequestBody AssignVehicleRequest request) {
         try {
-            int result = sysTaskVehicleService.assignVehicleToTask(request.getTaskId(), request.getVehicleId(), request.getRemark());
+            int result = sysTaskVehicleService.assignVehicleToTask(request.getTaskId(), request.getVehicleId(), request.getRemark(), getUserId(), getUsername());
             if (result > 0) {
                 return success("鍒嗛厤鎴愬姛");
             } else {
@@ -161,7 +161,7 @@
     @PostMapping("/assign-batch")
     public AjaxResult assignVehicles(@RequestBody BatchAssignVehicleRequest request) {
         try {
-            int result = sysTaskVehicleService.assignMultipleVehiclesToTask(request.getTaskId(), request.getVehicleIds(), request.getRemark());
+            int result = sysTaskVehicleService.assignMultipleVehiclesToTask(request.getTaskId(), request.getVehicleIds(), request.getRemark(), getUserId(), getUsername());
             if (result > 0) {
                 return success("鎵归噺鍒嗛厤鎴愬姛锛屽叡鍒嗛厤 " + result + " 杈嗚溅");
             } else {
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index 8ab857f..105bc73 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -247,4 +247,19 @@
   # 瀵硅处閰嶇疆
   reconciliation:
     enabled: true
-    cron: "0 0 2 * * ?"
\ No newline at end of file
+    cron: "0 0 2 * * ?"
+
+# 鐭俊鏈嶅姟閰嶇疆
+sms:
+  # 鏄惁鍚敤鐭俊鍔熻兘锛堝彲鍦ㄦ暟鎹簱sys_config涓�氳繃sms.enabled閰嶇疆锛屼紭鍏堢骇鏇撮珮锛�
+  enabled: false
+  # 鐭俊鏈嶅姟鍦板潃
+  address: sms.izjun.com:8001
+  # 鐭俊璐﹀彿鐢ㄦ埛鍚�
+  userName: gdmhhy
+  # 鐭俊璐﹀彿瀵嗙爜
+  password: lLsZyz3YVYKQ
+  # 鐭俊绛惧悕
+  signName: "銆�966120鎬ユ晳杞繍銆�"
+  # 浠诲姟鍒嗛厤閫氱煡妯℃澘
+  taskAssignTemplate: "鎮ㄦ湁鏂扮殑杞繍浠诲姟锛屽嚭鍙戝湴:{departure}锛岀洰鐨勫湴:{destination}锛岃鍙婃椂澶勭悊銆�"
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/resources/logback.xml b/ruoyi-admin/src/main/resources/logback.xml
index a8c526e..8c3fc6f 100644
--- a/ruoyi-admin/src/main/resources/logback.xml
+++ b/ruoyi-admin/src/main/resources/logback.xml
@@ -4,6 +4,10 @@
 	<property name="log.path" value="./logs" />
     <!-- 鏃ュ織杈撳嚭鏍煎紡 -->
 	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+	<!-- 鍗曚釜鏃ュ織鏂囦欢鏈�澶уぇ灏� -->
+	<property name="log.maxFileSize" value="100MB" />
+	<!-- 鏃ュ織鏂囦欢鎬诲ぇ灏忎笂闄� -->
+	<property name="log.totalSizeCap" value="10GB" />
 
 	<!-- 鎺у埗鍙拌緭鍑� -->
 	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
@@ -15,12 +19,16 @@
 	<!-- 绯荤粺鏃ュ織杈撳嚭 -->
 	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
 	    <file>${log.path}/sys-info.log</file>
-        <!-- 寰幆鏀跨瓥锛氬熀浜庢椂闂村垱寤烘棩蹇楁枃浠� -->
-		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+        <!-- 寰幆鏀跨瓥锛氬熀浜庢椂闂村拰澶у皬鍒涘缓鏃ュ織鏂囦欢 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
             <!-- 鏃ュ織鏂囦欢鍚嶆牸寮� -->
-			<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
-			<!-- 鏃ュ織鏈�澶х殑鍘嗗彶 60澶� -->
-			<maxHistory>60</maxHistory>
+			<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+			<!-- 鍗曚釜鏃ュ織鏂囦欢鏈�澶уぇ灏� -->
+			<maxFileSize>${log.maxFileSize}</maxFileSize>
+			<!-- 鏃ュ織鏈�澶х殑鍘嗗彶 30澶╋紙鐢熶骇鐜鍑忓皯淇濈暀鏃堕棿锛� -->
+			<maxHistory>30</maxHistory>
+			<!-- 鏃ュ織鏂囦欢鎬诲ぇ灏忎笂闄� -->
+			<totalSizeCap>${log.totalSizeCap}</totalSizeCap>
 		</rollingPolicy>
 		<encoder>
 			<pattern>${log.pattern}</pattern>
@@ -37,12 +45,16 @@
 	
 	<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
 	    <file>${log.path}/sys-error.log</file>
-        <!-- 寰幆鏀跨瓥锛氬熀浜庢椂闂村垱寤烘棩蹇楁枃浠� -->
-        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+        <!-- 寰幆鏀跨瓥锛氬熀浜庢椂闂村拰澶у皬鍒涘缓鏃ュ織鏂囦欢 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
             <!-- 鏃ュ織鏂囦欢鍚嶆牸寮� -->
-            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
-			<!-- 鏃ュ織鏈�澶х殑鍘嗗彶 60澶� -->
+            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+			<!-- 鍗曚釜鏃ュ織鏂囦欢鏈�澶уぇ灏� -->
+			<maxFileSize>${log.maxFileSize}</maxFileSize>
+			<!-- 鏃ュ織鏈�澶х殑鍘嗗彶 60澶╋紙閿欒鏃ュ織淇濈暀鏇撮暱鏃堕棿锛� -->
 			<maxHistory>60</maxHistory>
+			<!-- 鏃ュ織鏂囦欢鎬诲ぇ灏忎笂闄� -->
+			<totalSizeCap>5GB</totalSizeCap>
         </rollingPolicy>
         <encoder>
             <pattern>${log.pattern}</pattern>
@@ -60,31 +72,81 @@
 	<!-- 鐢ㄦ埛璁块棶鏃ュ織杈撳嚭  -->
     <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
 		<file>${log.path}/sys-user.log</file>
-        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
             <!-- 鎸夊ぉ鍥炴粴 daily -->
-            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
-            <!-- 鏃ュ織鏈�澶х殑鍘嗗彶 60澶� -->
-            <maxHistory>60</maxHistory>
+            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+			<!-- 鍗曚釜鏃ュ織鏂囦欢鏈�澶уぇ灏� -->
+			<maxFileSize>${log.maxFileSize}</maxFileSize>
+            <!-- 鏃ュ織鏈�澶х殑鍘嗗彶 30澶� -->
+            <maxHistory>30</maxHistory>
+			<!-- 鏃ュ織鏂囦欢鎬诲ぇ灏忎笂闄� -->
+			<totalSizeCap>5GB</totalSizeCap>
         </rollingPolicy>
         <encoder>
             <pattern>${log.pattern}</pattern>
         </encoder>
     </appender>
 	
-	<!-- 绯荤粺妯″潡鏃ュ織绾у埆鎺у埗  -->
-	<logger name="com.ruoyi" level="info" />
-	<!-- Spring鏃ュ織绾у埆鎺у埗  -->
-	<logger name="org.springframework" level="warn" />
-
-	<root level="info">
-		<appender-ref ref="console" />
-	</root>
+	<!-- 鎱QL鏃ュ織鍗曠嫭杈撳嚭锛堜究浜庢�ц兘鐩戞帶鍜屼紭鍖栵級 -->
+	<appender name="slow-sql" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<file>${log.path}/slow-sql.log</file>
+		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+			<fileNamePattern>${log.path}/slow-sql.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+			<maxFileSize>50MB</maxFileSize>
+			<maxHistory>15</maxHistory>
+			<totalSizeCap>2GB</totalSizeCap>
+		</rollingPolicy>
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+	</appender>
 	
-	<!--绯荤粺鎿嶄綔鏃ュ織-->
-    <root level="info">
-        <appender-ref ref="file_info" />
-        <appender-ref ref="file_error" />
-    </root>
+	<!-- 寮�鍙戠幆澧冮厤缃� -->
+	<springProfile name="dev,test">
+		<!-- 绯荤粺妯″潡鏃ュ織绾у埆鎺у埗  -->
+		<logger name="com.ruoyi" level="info" />
+		<!-- Spring鏃ュ織绾у埆鎺у埗  -->
+		<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" />
+			<appender-ref ref="file_error" />
+		</root>
+	</springProfile>
+	
+	<!-- 鐢熶骇鐜閰嶇疆 -->
+	<springProfile name="prod">
+		<!-- 绯荤粺妯″潡鏃ュ織绾у埆鎺у埗锛堢敓浜х幆澧冭皟鏁翠负WARN锛屽噺灏慖NFO鏃ュ織锛� -->
+		<logger name="com.ruoyi" level="warn" />
+		<!-- Spring鏃ュ織绾у埆鎺у埗  -->
+		<logger name="org.springframework" level="error" />
+		<!-- Druid鏁版嵁婧愭棩蹇楁帶鍒� -->
+		<logger name="com.alibaba.druid" level="error" />
+		<!-- MyBatis鏃ュ織鎺у埗 -->
+		<logger name="org.mybatis" level="error" />
+		<logger name="org.apache.ibatis" level="error" />
+		<!-- HTTP瀹㈡埛绔棩蹇楁帶鍒� -->
+		<logger name="org.apache.http" level="error" />
+		
+		<!-- 鐗瑰畾涓氬姟妯″潡淇濈暀INFO绾у埆锛堟牴鎹渶瑕佽皟鏁达級 -->
+		<logger name="com.ruoyi.system.service.impl.LegacySystemSyncServiceImpl" level="info" />
+		<logger name="com.ruoyi.system.service.impl.VehicleGpsSegmentMileageServiceImpl" level="info" />
+		
+		<!-- 鎱QL鏃ュ織锛圖ruid鐩戞帶锛� -->
+		<logger name="druid.sql.Statement" level="warn" additivity="false">
+			<appender-ref ref="slow-sql" />
+		</logger>
+		
+		<root level="warn">
+			<!-- 鐢熶骇鐜涓嶈緭鍑哄埌鎺у埗鍙帮紝鍑忓皯璧勬簮娑堣�� -->
+			<!-- <appender-ref ref="console" /> -->
+			<appender-ref ref="file_info" />
+			<appender-ref ref="file_error" />
+		</root>
+	</springProfile>
 	
 	<!--绯荤粺鐢ㄦ埛鎿嶄綔鏃ュ織-->
     <logger name="sys-user" level="info">
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/SmsConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/SmsConfig.java
new file mode 100644
index 0000000..90ff2d9
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/SmsConfig.java
@@ -0,0 +1,81 @@
+package com.ruoyi.common.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 鐭俊鏈嶅姟閰嶇疆绫�
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+@Component
+@ConfigurationProperties(prefix = "sms")
+public class SmsConfig {
+
+    /** 鏄惁鍚敤鐭俊鍔熻兘 */
+    private boolean enabled = false;
+
+    /** 鐭俊鏈嶅姟鍦板潃 */
+    private String address = "sms.izjun.com:8001";
+
+    /** 鐭俊璐﹀彿鐢ㄦ埛鍚� */
+    private String userName;
+
+    /** 鐭俊璐﹀彿瀵嗙爜 */
+    private String password;
+
+    /** 鐭俊绛惧悕 */
+    private String signName = "銆�966120鎬ユ晳杞繍銆�";
+
+    /** 浠诲姟鍒嗛厤閫氱煡妯℃澘 */
+    private String taskAssignTemplate = "鎮ㄦ湁鏂扮殑杞繍浠诲姟(缂栧彿:{taskCode})锛屽嚭鍙戝湴:{departure}锛岀洰鐨勫湴:{destination}锛岃鍙婃椂澶勭悊銆�";
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public String getAddress() {
+        return address;
+    }
+
+    public void setAddress(String address) {
+        this.address = address;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getSignName() {
+        return signName;
+    }
+
+    public void setSignName(String signName) {
+        this.signName = signName;
+    }
+
+    public String getTaskAssignTemplate() {
+        return taskAssignTemplate;
+    }
+
+    public void setTaskAssignTemplate(String taskAssignTemplate) {
+        this.taskAssignTemplate = taskAssignTemplate;
+    }
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/SmsUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SmsUtils.java
new file mode 100644
index 0000000..a457f5a
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SmsUtils.java
@@ -0,0 +1,244 @@
+package com.ruoyi.common.utils;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.common.utils.http.HttpUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鐭俊鍙戦�佸伐鍏风被
+ * 鍙傝�働HP鐗堟湰鐨凷msUtils瀹炵幇
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+public class SmsUtils {
+
+    private static final Logger log = LoggerFactory.getLogger(SmsUtils.class);
+
+
+    /**
+     * 鍙戦�� POST 璇锋眰
+     *
+     * @param url  鏈嶅姟鍣ㄥ湴鍧�
+     * @param data 璇锋眰鍙傛暟
+     * @return 鍝嶅簲鍐呭
+     * @throws IOException
+     */
+    public static String post(String url, String data) throws IOException {
+        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
+        connection.setRequestMethod("POST");
+        connection.setRequestProperty("Content-Type", "application/json");
+        connection.setDoOutput(true);
+        try (OutputStream os = connection.getOutputStream()) {
+            os.write(data.getBytes(StandardCharsets.UTF_8));
+            os.flush();
+        }
+        StringBuilder response = new StringBuilder();
+        try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
+            String line;
+            while ((line = br.readLine()) != null) {
+                response.append(line);
+            }
+        }
+        return response.toString();
+    }
+
+    /**
+     * 璁$畻 MD5 鍊�
+     *
+     * @param str 瀛楃涓�
+     * @return MD5 鍊�
+     * @throws NoSuchAlgorithmException
+     */
+    public static String md5(String str) throws NoSuchAlgorithmException {
+        MessageDigest md = MessageDigest.getInstance("MD5");
+        byte[] array = md.digest(str.getBytes(StandardCharsets.UTF_8));
+        StringBuilder sb = new StringBuilder();
+        for (byte item : array) {
+            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 鑾峰彇褰撳墠鏃堕棿鎴筹紙姣锛�
+     * 
+     * @return 鏃堕棿鎴冲瓧绗︿覆
+     */
+    public static Long getTimestamp() {
+        return  System.currentTimeMillis();
+    }
+
+    /**
+     * 鐢熸垚绛惧悕
+     * 绛惧悕绠楁硶锛歁D5(userName + password + timestamp)
+     * 
+     * @param userName 鐢ㄦ埛鍚�
+     * @param password 瀵嗙爜
+     * @param timestamp 鏃堕棿鎴�
+     * @return 绛惧悕瀛楃涓�
+     */
+    public static String generateSign(String userName, String password, Long timestamp) throws NoSuchAlgorithmException {
+        // 鏃堕棿鎴�
+
+
+        // 璁$畻 sign 鍙傛暟
+        String sign = md5(userName + timestamp + md5(password));
+        return sign;
+    }
+
+    /**
+     * 鍙戦�丠TTP POST璇锋眰
+     * 
+     * @param url 璇锋眰鍦板潃
+     * @param data 璇锋眰鏁版嵁
+     * @return 鍝嶅簲缁撴灉
+     */
+    public static JSONObject sendPost(String url, Map<String, Object> data) {
+        try {
+            String jsonData = JSON.toJSONString(data);
+            log.debug("鐭俊API璇锋眰锛孶RL锛歿}锛屾暟鎹細{}", url, jsonData);
+            
+            String response = post(url,jsonData);;//HttpUtils.sendPost(url, jsonData, "application/json");
+            log.debug("鐭俊API鍝嶅簲锛歿}", response);
+            
+            if (StringUtils.isNotEmpty(response)) {
+                return JSON.parseObject(response);
+            }
+        } catch (Exception e) {
+            log.error("鐭俊API璇锋眰澶辫触锛孶RL锛歿}", url, e);
+        }
+        return null;
+    }
+
+    /**
+     * 缇ゅ彂鐭俊
+     * 
+     * @param address 鐭俊鏈嶅姟鍦板潃
+     * @param userName 璐﹀彿
+     * @param password 瀵嗙爜
+     * @param phoneList 鎵嬫満鍙峰垪琛紝澶氫釜鐢ㄩ�楀彿鍒嗛殧
+     * @param content 鐭俊鍐呭
+     * @return 鍙戦�佺粨鏋�
+     */
+    public static JSONObject sendSmsMass(String address, String userName, String password,
+                                          String phoneList, String content) throws NoSuchAlgorithmException{
+        Long timestamp = getTimestamp();
+        String sign = generateSign(userName, password, timestamp);
+        String url = "http://" + address + "/sms/api/sendMessageMass";
+
+        Map<String, Object> data = new HashMap<>();
+        data.put("userName", userName);
+        data.put("content", content);
+        data.put("phoneList", phoneList);
+        data.put("timestamp", timestamp);
+        data.put("sign", sign);
+        data.put("sendTime", "");
+        data.put("extCode", "");
+        data.put("callData", "");
+
+        return sendPost(url, data);
+    }
+
+    /**
+     * 鍗曟潯/鐐瑰鐐圭煭淇″彂閫�
+     * 
+     * @param address 鐭俊鏈嶅姟鍦板潃
+     * @param userName 璐﹀彿
+     * @param password 瀵嗙爜
+     * @param messageList 娑堟伅鍒楄〃 [{phone: "鎵嬫満鍙�", content: "鍐呭"}, ...]
+     * @return 鍙戦�佺粨鏋�
+     */
+    public static JSONObject sendSmsOne(String address, String userName, String password,
+                                         List<Map<String, String>> messageList) throws NoSuchAlgorithmException {
+        Long timestamp = getTimestamp();
+        String sign = generateSign(userName, password, timestamp);
+        String url = "http://" + address + "/sms/api/sendMessageOne";
+
+        Map<String, Object> data = new HashMap<>();
+        data.put("userName", userName);
+        data.put("timestamp", timestamp);
+        data.put("sign", sign);
+        data.put("messageList", messageList);
+
+        return sendPost(url, data);
+    }
+
+    /**
+     * 鍙戦�佸崟鏉$煭淇★紙绠�鍖栨柟娉曪級
+     * 
+     * @param address 鐭俊鏈嶅姟鍦板潃
+     * @param userName 璐﹀彿
+     * @param password 瀵嗙爜
+     * @param phone 鎵嬫満鍙�
+     * @param content 鐭俊鍐呭
+     * @return 鍙戦�佺粨鏋�
+     */
+    public static JSONObject sendSms(String address, String userName, String password,
+                                      String phone, String content)  throws NoSuchAlgorithmException{
+        List<Map<String, String>> messageList = new ArrayList<>();
+        messageList.add(new HashMap<String, String>() {{
+            put("phone", phone);
+            put("content", content);
+        }});
+        return sendSmsOne(address, userName, password, messageList);
+    }
+
+    /**
+     * 鏌ヨ璐︽埛浣欓
+     * 
+     * @param address 鐭俊鏈嶅姟鍦板潃
+     * @param userName 璐﹀彿
+     * @param password 瀵嗙爜
+     * @return 浣欓淇℃伅
+     */
+    public static JSONObject getBalance(String address, String userName, String password) throws NoSuchAlgorithmException {
+        Long timestamp = getTimestamp();
+        String sign = generateSign(userName, password, timestamp);
+        String url = "http://" + address + "/sms/api/getBalance";
+
+        Map<String, Object> data = new HashMap<>();
+        data.put("userName", userName);
+        data.put("timestamp", timestamp);
+        data.put("sign", sign);
+
+        return sendPost(url, data);
+    }
+
+    /**
+     * 鑾峰彇鍙戦�佹姤鍛�
+     * 
+     * @param address 鐭俊鏈嶅姟鍦板潃
+     * @param userName 璐﹀彿
+     * @param password 瀵嗙爜
+     * @return 鍙戦�佹姤鍛�
+     */
+    public static JSONObject getReport(String address, String userName, String password) throws NoSuchAlgorithmException {
+        Long timestamp = getTimestamp();
+        String sign = generateSign(userName, password, timestamp);
+        String url = "http://" + address + "/sms/api/getReport";
+
+        Map<String, Object> data = new HashMap<>();
+        data.put("userName", userName);
+        data.put("timestamp", timestamp);
+        data.put("sign", sign);
+
+        return sendPost(url, data);
+    }
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/civilAviation/ServiceOrderUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/civilAviation/ServiceOrderUtil.java
index da4efa0..bdc72da 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/civilAviation/ServiceOrderUtil.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/civilAviation/ServiceOrderUtil.java
@@ -49,7 +49,7 @@
             return null;
         }
 
-        log.info("鎺ュ彛鍦板潃锛�"+addServiceOrederAPI);
+//        log.info("鎺ュ彛鍦板潃锛�"+addServiceOrederAPI);
 
         //璁$畻鏃堕棿鎴冲拰绛惧悕
         long unixTime = System.currentTimeMillis() / 1000; // 鑾峰彇褰撳墠鏃堕棿鎴筹紙绉掞級  Long.valueOf("1746444543") ;//
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java
index 9770af6..1eb1cb1 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java
@@ -23,7 +23,7 @@
      */
     public static void setDataSourceType(String dsType)
     {
-        log.info("鍒囨崲鍒皗}鏁版嵁婧�", dsType);
+//        log.info("鍒囨崲鍒皗}鏁版嵁婧�", dsType);
         CONTEXT_HOLDER.set(dsType);
     }
 
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CleanVehicleGpsTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CleanVehicleGpsTask.java
index 321d326..f177d1d 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CleanVehicleGpsTask.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CleanVehicleGpsTask.java
@@ -21,9 +21,9 @@
      */
     public void cleanVehicleGpsData() {
         try {
-            log.info("寮�濮嬫竻鐞嗚溅杈咷PS鍘嗗彶鏁版嵁");
+//            log.info("寮�濮嬫竻鐞嗚溅杈咷PS鍘嗗彶鏁版嵁");
             int count = vehicleGpsService.deleteVehicleGpsBeforeDate();
-            log.info("娓呯悊杞﹁締GPS鍘嗗彶鏁版嵁瀹屾垚锛屽叡娓呯悊{}鏉¤褰�", count);
+//            log.info("娓呯悊杞﹁締GPS鍘嗗彶鏁版嵁瀹屾垚锛屽叡娓呯悊{}鏉¤褰�", count);
         } catch (Exception e) {
             log.error("娓呯悊杞﹁締GPS鍘嗗彶鏁版嵁寮傚父", e);
         }
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CmsVehicleSyncTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CmsVehicleSyncTask.java
index 1694256..b006eb5 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CmsVehicleSyncTask.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CmsVehicleSyncTask.java
@@ -37,7 +37,7 @@
     private IVehicleGpsService vehicleGpsService;
 
     public void syncVehicleInfo() {
-        log.info("寮�濮嬪悓姝MS杞﹁締淇℃伅");
+//        log.info("寮�濮嬪悓姝MS杞﹁締淇℃伅");
         try {
             // 鑾峰彇CMS鎵�鏈夎溅杈嗕俊鎭�
             CmsVehicleDeviceListResponse response = cmsGpsCollectService.getDeviceByVehicle(null);
@@ -94,7 +94,7 @@
                                 vehicleInfo.setStatus("0");
                                 vehicleInfoService.insertVehicleInfo(vehicleInfo);
                                 syncCarCount++;
-                                log.info("鏂板CMS杞﹁締: {}", plateNo);
+//                                log.info("鏂板CMS杞﹁締: {}", plateNo);
                             } else {
                                 // 鏇存柊杞﹁締 - 浠呮洿鏂板繀瑕佸瓧娈�,閬垮厤瑙﹀彂鍏宠仈琛ㄦ搷浣�
                                 vehicleInfo.setDeviceId(vehicle.getDid());
@@ -103,7 +103,7 @@
                                 vehicleInfo.setDeptIds(null); // 涓嶆洿鏂伴儴闂ㄥ叧鑱�,閬垮厤姝婚攣
                                 vehicleInfoService.updateVehicleInfo(vehicleInfo);
                                 syncCarCount++;
-                                log.debug("鏇存柊CMS杞﹁締: {}", plateNo);
+//                                log.debug("鏇存柊CMS杞﹁締: {}", plateNo);
                             }
                             success = true;
                         } catch (org.springframework.dao.DeadlockLoserDataAccessException e) {
@@ -127,8 +127,8 @@
 
 
 
-            log.info("鎴愬姛鍚屾{}涓狢MS杞﹁締淇℃伅", syncCarCount);
-            log.info("CMS杞﹁締淇℃伅鍚屾瀹屾垚");
+//            log.info("鎴愬姛鍚屾{}涓狢MS杞﹁締淇℃伅", syncCarCount);
+//            log.info("CMS杞﹁締淇℃伅鍚屾瀹屾垚");
         } catch (Exception e) {
             log.error("鍚屾CMS杞﹁締淇℃伅寮傚父", e);
         }
@@ -154,7 +154,7 @@
      * 鍚屾CMS杞﹁締浣嶇疆淇℃伅
      */
     public void syncVehicleLocation() {
-        log.info("寮�濮嬪悓姝MS杞﹁締浣嶇疆淇℃伅");
+//        log.info("寮�濮嬪悓姝MS杞﹁締浣嶇疆淇℃伅");
         try {
                 //鍏堣幏寰楁湰鍦癈MS涓婄殑鎵�鏈塁MS杞﹁締
                 VehicleInfo query = new VehicleInfo();
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/GpsSyncTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/GpsSyncTask.java
index ac0f9f2..5292236 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/GpsSyncTask.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/GpsSyncTask.java
@@ -41,7 +41,7 @@
      */
     public void syncGpsData() {
         try {
-            log.info("寮�濮嬪悓姝PS鏁版嵁...");
+//            log.info("寮�濮嬪悓姝PS鏁版嵁...");
 
             // 1. 鑾峰彇鎵�鏈夎溅杈嗕俊鎭�
             List<VehicleInfo> vehicleList = vehicleInfoService.selectVehicleInfoList(new VehicleInfo());
@@ -68,7 +68,7 @@
                 }
             }
 
-            log.info("GPS鏁版嵁鍚屾瀹屾垚");
+//            log.info("GPS鏁版嵁鍚屾瀹屾垚");
         } catch (Exception e) {
             log.error("GPS鏁版嵁鍚屾澶辫触: {}", e.getMessage());
         }
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 c55b98f..3c724a9 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
@@ -168,10 +168,10 @@
      * 3. 鏈夎皟搴﹀崟ID鍜屾湇鍔″崟ID
      */
     public void syncPendingAttachments() {
-        log.info("寮�濮嬫墽琛屼换鍔¢檮浠跺悓姝ュ畾鏃朵换鍔�");
+//        log.info("寮�濮嬫墽琛屼换鍔¢檮浠跺悓姝ュ畾鏃朵换鍔�");
         try {
             int successCount = taskAttachmentSyncService.batchSyncPendingAttachments();
-            log.info("浠诲姟闄勪欢鍚屾瀹屾垚锛屾垚鍔熷悓姝�: {} 涓檮浠�", successCount);
+//            log.info("浠诲姟闄勪欢鍚屾瀹屾垚锛屾垚鍔熷悓姝�: {} 涓檮浠�", successCount);
         } catch (Exception e) {
             log.error("浠诲姟闄勪欢鍚屾寮傚父", e);
         }
@@ -188,10 +188,10 @@
      * cron琛ㄨ揪寮�: 0 0/10 * * * ? (姣�10鍒嗛挓鎵ц涓�娆�)
      */
     public void syncPaymentToLegacy() {
-        log.info("寮�濮嬫墽琛屾敮浠樹俊鎭悓姝ュ畾鏃朵换鍔★紙鏂扮郴缁� -> 鏃х郴缁燂級");
+//        log.info("寮�濮嬫墽琛屾敮浠樹俊鎭悓姝ュ畾鏃朵换鍔★紙鏂扮郴缁� -> 鏃х郴缁燂級");
         try {
             int successCount = paymentSyncService.batchSyncPaymentToLegacy();
-            log.info("鏀粯淇℃伅鍚屾瀹屾垚锛屾垚鍔熷悓姝�: {} 鏉¤褰�", successCount);
+//            log.info("鏀粯淇℃伅鍚屾瀹屾垚锛屾垚鍔熷悓姝�: {} 鏉¤褰�", successCount);
         } catch (Exception e) {
             log.error("鏀粯淇℃伅鍚屾寮傚父", e);
         }
@@ -208,10 +208,10 @@
      * cron琛ㄨ揪寮�: 0 0/15 * * * ? (姣�15鍒嗛挓鎵ц涓�娆�)
      */
     public void syncPaymentFromLegacy() {
-        log.info("寮�濮嬫墽琛屾敮浠樹俊鎭悓姝ュ畾鏃朵换鍔★紙鏃х郴缁� -> 鏂扮郴缁燂級");
+//        log.info("寮�濮嬫墽琛屾敮浠樹俊鎭悓姝ュ畾鏃朵换鍔★紙鏃х郴缁� -> 鏂扮郴缁燂級");
         try {
             int successCount = paymentSyncService.batchSyncPaymentFromLegacy();
-            log.info("鏀粯淇℃伅鍚屾瀹屾垚锛屾垚鍔熷悓姝�: {} 鏉¤褰�", successCount);
+//            log.info("鏀粯淇℃伅鍚屾瀹屾垚锛屾垚鍔熷悓姝�: {} 鏉¤褰�", successCount);
         } catch (Exception e) {
             log.error("鏀粯淇℃伅鍚屾寮傚父", e);
         }
@@ -228,10 +228,10 @@
      * cron琛ㄨ揪寮�: 0 0/10 * * * ? (姣�10鍒嗛挓鎵ц涓�娆�)
      */
     public void syncAdditionalFeeToLegacy() {
-        log.info("寮�濮嬫墽琛屾柊绯荤粺闄勫姞璐圭敤鍚屾瀹氭椂浠诲姟锛堟柊绯荤粺 -> 鏃х郴缁燂級");
+//        log.info("寮�濮嬫墽琛屾柊绯荤粺闄勫姞璐圭敤鍚屾瀹氭椂浠诲姟锛堟柊绯荤粺 -> 鏃х郴缁燂級");
         try {
             int successCount = additionalFeeSyncService.batchSyncAdditionalFeeToLegacy();
-            log.info("闄勫姞璐圭敤鍚屾瀹屾垚锛屾垚鍔熷悓姝�: {} 鏉¤褰�", successCount);
+//            log.info("闄勫姞璐圭敤鍚屾瀹屾垚锛屾垚鍔熷悓姝�: {} 鏉¤褰�", successCount);
         } catch (Exception e) {
             log.error("闄勫姞璐圭敤鍚屾寮傚父", e);
         }
@@ -248,11 +248,11 @@
      * cron琛ㄨ揪寮忥細 0 0/15 * * * ? (姣�15鍒嗛挓鎵ц涓�娆�)
      */
     public void syncAdditionalFeeFromLegacy() {
-        log.info("寮�濮嬫墽琛屾棫绯荤粺闄勫姞璐圭敤鍚屾瀹氭椂浠诲姟锛堟棫绯荤粺 -> 鏂扮郴缁燂級");
+//        log.info("寮�濮嬫墽琛屾棫绯荤粺闄勫姞璐圭敤鍚屾瀹氭椂浠诲姟锛堟棫绯荤粺 -> 鏂扮郴缁燂級");
         try {
             // 鍚屾鏈�杩�24灏忔椂鍐呯殑璁板綍
             int successCount = additionalFeeSyncService.batchSyncAdditionalFeeFromLegacy(72);
-            log.info("鏃х郴缁熼檮鍔犺垂鐢ㄥ悓姝ュ畬鎴愶紝鎴愬姛鍚屾: {} 鏉¤褰�", successCount);
+//            log.info("鏃х郴缁熼檮鍔犺垂鐢ㄥ悓姝ュ畬鎴愶紝鎴愬姛鍚屾: {} 鏉¤褰�", successCount);
         } catch (Exception e) {
             log.error("闄勫姞璐圭敤鍚屾寮傚父", e);
         }
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 21d8fdf..9e27a30 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
@@ -24,10 +24,10 @@
      * 鍚屾7澶╁墠鐨勬棫绯荤粺杞繍鍗曟暟鎹�
      */
     public void syncTransferOrders7Days() {
-        log.info("寮�濮嬫墽琛�7澶╁墠鏃х郴缁熻浆杩愬崟鍚屾浠诲姟");
+//        log.info("寮�濮嬫墽琛�7澶╁墠鏃х郴缁熻浆杩愬崟鍚屾浠诲姟");
         try {
             int count = legacyTransferSyncService.syncLegacyTransferOrders(7);
-            log.info("7澶╁墠鏃х郴缁熻浆杩愬崟鍚屾浠诲姟鎵ц瀹屾垚锛屽悓姝ユ暟閲�: {}", count);
+//            log.info("7澶╁墠鏃х郴缁熻浆杩愬崟鍚屾浠诲姟鎵ц瀹屾垚锛屽悓姝ユ暟閲�: {}", count);
         } catch (Exception e) {
             log.error("7澶╁墠鏃х郴缁熻浆杩愬崟鍚屾浠诲姟鎵ц寮傚父", e);
         }
@@ -39,11 +39,11 @@
      * @param daysAgo 澶╂暟
      */
     public void syncTransferOrders(String daysAgo) {
-        log.info("寮�濮嬫墽琛寋}澶╁墠鏃х郴缁熻浆杩愬崟鍚屾浠诲姟", daysAgo);
+//        log.info("寮�濮嬫墽琛寋}澶╁墠鏃х郴缁熻浆杩愬崟鍚屾浠诲姟", daysAgo);
         try {
             int days = Integer.parseInt(daysAgo);
             int count = legacyTransferSyncService.syncLegacyTransferOrders(days);
-            log.info("{}澶╁墠鏃х郴缁熻浆杩愬崟鍚屾浠诲姟鎵ц瀹屾垚锛屽悓姝ユ暟閲�: {}", daysAgo, count);
+//            log.info("{}澶╁墠鏃х郴缁熻浆杩愬崟鍚屾浠诲姟鎵ц瀹屾垚锛屽悓姝ユ暟閲�: {}", daysAgo, count);
         } catch (Exception e) {
             log.error("{}澶╁墠鏃х郴缁熻浆杩愬崟鍚屾浠诲姟鎵ц寮傚父", daysAgo, e);
         }
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacyVehicleSyncTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacyVehicleSyncTask.java
index dea2b2e..46caf06 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacyVehicleSyncTask.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacyVehicleSyncTask.java
@@ -46,7 +46,7 @@
             
             if (result.isSuccess() || result.isWarn())
             {
-                log.info("鏃х郴缁熻溅杈嗗悓姝ヤ换鍔℃墽琛屾垚鍔�: {}", result.get("msg"));
+//                log.info("鏃х郴缁熻溅杈嗗悓姝ヤ换鍔℃墽琛屾垚鍔�: {}", result.get("msg"));
             }
             else if (result.isError())
             {
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/OaSyncTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/OaSyncTask.java
index 97d811a..2701b55 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/OaSyncTask.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/OaSyncTask.java
@@ -74,29 +74,29 @@
             log.info("##########寮�濮嬫墽琛孫A鏁版嵁鍚屾瀹氭椂浠诲姟##########");
             
             // 绗竴姝ワ細鍚屾閮ㄩ棬
-            log.info("銆愭楠�1/2銆戝紑濮嬪悓姝ラ儴闂ㄦ暟鎹�...");
+//            log.info("銆愭楠�1/2銆戝紑濮嬪悓姝ラ儴闂ㄦ暟鎹�...");
             AjaxResult deptResult = departmentSyncService.syncBranchDepartments(this.getDept(),this.getServiceOrdCode(),this.getDispatchOrdCode(),this.getAddressList());
             
             if (deptResult.get("code").equals(200))
             {
-                log.info("銆愭楠�1/2銆戦儴闂ㄥ悓姝ユ垚鍔�: {}", deptResult.get("msg"));
+//                log.info("銆愭楠�1/2銆戦儴闂ㄥ悓姝ユ垚鍔�: {}", deptResult.get("msg"));
                 
                 // 绗簩姝ワ細鍚屾鐢ㄦ埛锛堝彧鏈夐儴闂ㄥ悓姝ユ垚鍔熸墠鎵ц锛�
-                log.info("銆愭楠�2/2銆戝紑濮嬪悓姝ョ敤鎴锋暟鎹�...");
+//                log.info("銆愭楠�2/2銆戝紑濮嬪悓姝ョ敤鎴锋暟鎹�...");
                 AjaxResult userResult = userSyncService.syncOaUsers(this.getUserData());
                 
-                if (userResult.get("code").equals(200))
-                {
-                    log.info("銆愭楠�2/2銆戠敤鎴峰悓姝ユ垚鍔�: {}", userResult.get("msg"));
-                    log.info("##########OA鏁版嵁鍚屾瀹氭椂浠诲姟鎵ц瀹屾垚##########");
-                    log.info("鎬荤粨锛氶儴闂ㄥ悓姝ユ垚鍔燂紝鐢ㄦ埛鍚屾鎴愬姛");
-                }
-                else
-                {
-                    log.error("銆愭楠�2/2銆戠敤鎴峰悓姝ュけ璐�: {}", userResult.get("msg"));
-                    log.warn("##########OA鏁版嵁鍚屾瀹氭椂浠诲姟閮ㄥ垎瀹屾垚##########");
-                    log.warn("鎬荤粨锛氶儴闂ㄥ悓姝ユ垚鍔燂紝鐢ㄦ埛鍚屾澶辫触");
-                }
+//                if (userResult.get("code").equals(200))
+//                {
+//                    log.info("銆愭楠�2/2銆戠敤鎴峰悓姝ユ垚鍔�: {}", userResult.get("msg"));
+//                    log.info("##########OA鏁版嵁鍚屾瀹氭椂浠诲姟鎵ц瀹屾垚##########");
+//                    log.info("鎬荤粨锛氶儴闂ㄥ悓姝ユ垚鍔燂紝鐢ㄦ埛鍚屾鎴愬姛");
+//                }
+//                else
+//                {
+//                    log.error("銆愭楠�2/2銆戠敤鎴峰悓姝ュけ璐�: {}", userResult.get("msg"));
+//                    log.warn("##########OA鏁版嵁鍚屾瀹氭椂浠诲姟閮ㄥ垎瀹屾垚##########");
+//                    log.warn("鎬荤粨锛氶儴闂ㄥ悓姝ユ垚鍔燂紝鐢ㄦ埛鍚屾澶辫触");
+//                }
             }
             else
             {
@@ -118,7 +118,7 @@
      */
     public void syncOaData(String params)
     {
-        log.info("OA鏁版嵁鍚屾浠诲姟鍙傛暟: {}", params);
+//        log.info("OA鏁版嵁鍚屾浠诲姟鍙傛暟: {}", params);
         syncOaData();
     }
 
@@ -129,7 +129,7 @@
     {
         try
         {
-            log.info("==========寮�濮嬫墽琛岄儴闂ㄥ悓姝ヤ换鍔�==========");
+//            log.info("==========寮�濮嬫墽琛岄儴闂ㄥ悓姝ヤ换鍔�==========");
             AjaxResult result = departmentSyncService.syncBranchDepartments(this.getDept(),this.getServiceOrdCode(),this.getDispatchOrdCode(),this.getAddressList());
             
             if (result.get("code").equals(200))
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
index 866ba08..e70255f 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
@@ -103,7 +103,7 @@
             for (AOrderStatus orderStatus : orderStatusList) {
                 int count = tbOrdersService.checkServiceOrdIDExists(orderStatus.getServiceOrdID());
                 if (count > 0) {
-                    log.info("鏈嶅姟璁㈠崟ID瀛樺湪锛�" + orderStatus.getServiceOrdID());
+//                    log.info("鏈嶅姟璁㈠崟ID瀛樺湪锛�" + orderStatus.getServiceOrdID());
 
                     TbOrders tbOrders = tbOrdersService.selectTbOrdersByServiceOrdID(orderStatus.getServiceOrdID());
 
@@ -144,7 +144,7 @@
 
                             //濡傛灉杞︾墝鍙蜂笉涓虹┖
                             if(carLicense!=null && carLicense.length()>0){
-                                log.info("鏇存柊璁㈠崟-杞︾墝鍙峰叧鑱旇〃锛屻��" + tbOrders.getOrderID() + "銆戣溅鐗屽彿锛�" + carLicense);
+//                                log.info("鏇存柊璁㈠崟-杞︾墝鍙峰叧鑱旇〃锛屻��" + tbOrders.getOrderID() + "銆戣溅鐗屽彿锛�" + carLicense);
 
                                 //鏌ヨ鏄惁瀛樺湪鍏宠仈璁板綍
                                 TbVehicleOrder tbVehicleOrder = tbVehicleOrderService.selectTbVehicleOrderById(tbOrders.getOrderID());
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleGpsSegmentMileageTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleGpsSegmentMileageTask.java
index a317652..1ecff68 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleGpsSegmentMileageTask.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleGpsSegmentMileageTask.java
@@ -49,12 +49,12 @@
                 }
             }
             
-            logger.info("========== 寮�濮嬫墽琛孏PS鍒嗘閲岀▼琛ュ伩璁$畻 - 鍥炴函{}澶� ==========", lookbackDays);
+//            logger.info("========== 寮�濮嬫墽琛孏PS鍒嗘閲岀▼琛ュ伩璁$畻 - 鍥炴函{}澶� ==========", lookbackDays);
             
             // 鎵ц琛ュ伩璁$畻
             int successCount = segmentMileageService.compensateCalculation(lookbackDays);
             
-            logger.info("========== GPS鍒嗘閲岀▼琛ュ伩璁$畻瀹屾垚 - 鎴愬姛澶勭悊 {} 杈嗚溅 ==========", successCount);
+//            logger.info("========== GPS鍒嗘閲岀▼琛ュ伩璁$畻瀹屾垚 - 鎴愬姛澶勭悊 {} 杈嗚溅 ==========", successCount);
             
         } catch (Exception e) {
             logger.error("GPS鍒嗘閲岀▼琛ュ伩璁$畻澶辫触", e);
@@ -105,13 +105,13 @@
             
             // 娉ㄦ剰锛氭鏂规硶鍙绠楁渶杩戞椂闂存鐨勬暟鎹紝鍘嗗彶閬楁紡鏁版嵁鐢辫ˉ鍋挎満鍒跺鐞�
             
-            logger.info("寮�濮嬭绠桮PS鍒嗘閲岀▼ - 鏃堕棿鑼冨洿: {} 鍒� {}, 鏃堕棿娈甸棿闅�: {}鍒嗛挓", 
-                       startTime, endTime, segmentMinutes);
+//            logger.info("寮�濮嬭绠桮PS鍒嗘閲岀▼ - 鏃堕棿鑼冨洿: {} 鍒� {}, 鏃堕棿娈甸棿闅�: {}鍒嗛挓",
+//                       startTime, endTime, segmentMinutes);
             
             // 鎵归噺璁$畻
             int successCount = segmentMileageService.batchCalculateSegmentMileage(startTime, endTime);
             
-            logger.info("GPS鍒嗘閲岀▼璁$畻瀹屾垚 - 鎴愬姛澶勭悊 {} 杈嗚溅", successCount);
+//            logger.info("GPS鍒嗘閲岀▼璁$畻瀹屾垚 - 鎴愬姛澶勭悊 {} 杈嗚溅", successCount);
             
         } catch (Exception e) {
             logger.error("GPS鍒嗘閲岀▼璁$畻浠诲姟鎵ц澶辫触", e);
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleMileageStatsTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleMileageStatsTask.java
index 8619a88..68b9c2c 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleMileageStatsTask.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleMileageStatsTask.java
@@ -40,7 +40,7 @@
             // 鎵归噺璁$畻閲岀▼缁熻
             int successCount = vehicleMileageStatsService.batchCalculateMileageStats(yesterday);
             
-            logger.info("杞﹁締閲岀▼缁熻瀹氭椂浠诲姟鎵ц瀹屾垚 - 鎴愬姛缁熻: {} 杈嗚溅", successCount);
+//            logger.info("杞﹁締閲岀▼缁熻瀹氭椂浠诲姟鎵ц瀹屾垚 - 鎴愬姛缁熻: {} 杈嗚溅", successCount);
             
         } catch (Exception e) {
             logger.error("杞﹁締閲岀▼缁熻瀹氭椂浠诲姟鎵ц澶辫触", e);
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleSyncTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleSyncTask.java
index 6d80602..8f8212a 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleSyncTask.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleSyncTask.java
@@ -33,14 +33,14 @@
      */
     public void syncVehicleInfo() {
         try {
-            log.info("寮�濮嬪悓姝ヨ溅杈嗕俊鎭�...");
+//            log.info("寮�濮嬪悓姝ヨ溅杈嗕俊鎭�...");
 
             // 鑾峰彇璁惧鍒楄〃锛岃繖浼氳嚜鍔ㄦ洿鏂拌溅杈嗕俊鎭腑鐨勮澶嘔D
             GpsDeviceListResponse response = gpsCollectService.getDeviceList();
             // 鏇存柊杞﹁締璁惧ID
             updateVehicleDeviceIds(response);
 
-            log.info("杞﹁締淇℃伅鍚屾瀹屾垚");
+//            log.info("杞﹁締淇℃伅鍚屾瀹屾垚");
         } catch (Exception e) {
             log.error("杞﹁締淇℃伅鍚屾澶辫触: {}", e.getMessage());
         }
diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml
index 844fc1e..c18868b 100644
--- a/ruoyi-system/pom.xml
+++ b/ruoyi-system/pom.xml
@@ -44,12 +44,22 @@
             <artifactId>fastjson</artifactId>
             <version>1.2.83</version>
         </dependency>
+        
+        <!-- JUnit -->
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <scope>test</scope>
         </dependency>
+        
 
+
+        <!-- Spring Test -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-test</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/NotifySendLogController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/NotifySendLogController.java
new file mode 100644
index 0000000..054a6c6
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/NotifySendLogController.java
@@ -0,0 +1,69 @@
+package com.ruoyi.system.controller;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+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.core.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.system.domain.NotifySendLog;
+import com.ruoyi.system.service.INotifySendLogService;
+
+/**
+ * 閫氱煡鍙戦�佹棩蹇桟ontroller
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+@RestController
+@RequestMapping("/system/notify/log")
+public class NotifySendLogController extends BaseController
+{
+    @Autowired
+    private INotifySendLogService notifySendLogService;
+
+    /**
+     * 鏌ヨ閫氱煡鍙戦�佹棩蹇楀垪琛�
+     */
+    @PreAuthorize("@ss.hasPermi('system:notify:log:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(NotifySendLog notifySendLog)
+    {
+        startPage();
+        List<NotifySendLog> list = notifySendLogService.selectNotifySendLogList(notifySendLog);
+        return getDataTable(list);
+    }
+
+    /**
+     * 瀵煎嚭閫氱煡鍙戦�佹棩蹇楀垪琛�
+     */
+    @PreAuthorize("@ss.hasPermi('system:notify:log:export')")
+    @Log(title = "閫氱煡鍙戦�佹棩蹇�", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, NotifySendLog notifySendLog)
+    {
+        List<NotifySendLog> list = notifySendLogService.selectNotifySendLogList(notifySendLog);
+        ExcelUtil<NotifySendLog> util = new ExcelUtil<NotifySendLog>(NotifySendLog.class);
+        util.exportExcel(response, list, "閫氱煡鍙戦�佹棩蹇楁暟鎹�");
+    }
+
+    /**
+     * 鑾峰彇閫氱煡鍙戦�佹棩蹇楄缁嗕俊鎭�
+     */
+    @PreAuthorize("@ss.hasPermi('system:notify:log:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(notifySendLogService.selectNotifySendLogById(id));
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/NotifyTaskController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/NotifyTaskController.java
new file mode 100644
index 0000000..cef1978
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/NotifyTaskController.java
@@ -0,0 +1,69 @@
+package com.ruoyi.system.controller;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+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.core.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.system.domain.NotifyTask;
+import com.ruoyi.system.service.INotifyTaskService;
+
+/**
+ * 閫氱煡浠诲姟Controller
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+@RestController
+@RequestMapping("/system/notify/task")
+public class NotifyTaskController extends BaseController
+{
+    @Autowired
+    private INotifyTaskService notifyTaskService;
+
+    /**
+     * 鏌ヨ閫氱煡浠诲姟鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('system:notify:task:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(NotifyTask notifyTask)
+    {
+        startPage();
+        List<NotifyTask> list = notifyTaskService.selectNotifyTaskList(notifyTask);
+        return getDataTable(list);
+    }
+
+    /**
+     * 瀵煎嚭閫氱煡浠诲姟鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('system:notify:task:export')")
+    @Log(title = "閫氱煡浠诲姟", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, NotifyTask notifyTask)
+    {
+        List<NotifyTask> list = notifyTaskService.selectNotifyTaskList(notifyTask);
+        ExcelUtil<NotifyTask> util = new ExcelUtil<NotifyTask>(NotifyTask.class);
+        util.exportExcel(response, list, "閫氱煡浠诲姟鏁版嵁");
+    }
+
+    /**
+     * 鑾峰彇閫氱煡浠诲姟璇︾粏淇℃伅
+     */
+    @PreAuthorize("@ss.hasPermi('system:notify:task:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(notifyTaskService.selectNotifyTaskById(id));
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/SmsSendController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/SmsSendController.java
new file mode 100644
index 0000000..92fffad
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/SmsSendController.java
@@ -0,0 +1,25 @@
+package com.ruoyi.system.controller;
+
+import com.ruoyi.system.service.ISmsService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+
+@Service
+public class SmsSendController {
+    @Autowired
+    private ISmsService smsService;
+
+
+    public String sendSms(SmsSendReq req) {
+        if (smsService.sendSms(req.getPhone(), req.getContent())) {
+            return "鍙戦�佹垚鍔�";
+        } else {
+            return "鍙戦�佸け璐�";
+        }
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/SmsSendReq.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/SmsSendReq.java
new file mode 100644
index 0000000..f893e3e
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/SmsSendReq.java
@@ -0,0 +1,16 @@
+package com.ruoyi.system.controller;
+
+import lombok.Data;
+import lombok.NonNull;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+@Data
+public class SmsSendReq implements Serializable {
+
+    @NotBlank(message = "鎵嬫満鍙蜂笉鑳戒负绌�")
+    private String phone;
+    @NotBlank(message = "鍐呭涓嶈兘涓虹┖")
+    private String content;
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/NotifyChannelConfig.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/NotifyChannelConfig.java
new file mode 100644
index 0000000..862dae5
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/NotifyChannelConfig.java
@@ -0,0 +1,112 @@
+package com.ruoyi.system.domain;
+
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 閫氱煡娓犻亾閰嶇疆瀹炰綋绫�
+ * 鐢ㄤ簬閰嶇疆鍚勭被閫氱煡鍚敤鐨勫彂閫佹笭閬�
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+public class NotifyChannelConfig extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    // ==================== 娓犻亾甯搁噺 ====================
+    /** 娓犻亾锛氬井淇¤闃呮秷鎭� */
+    public static final String CHANNEL_WECHAT = "WECHAT";
+    /** 娓犻亾锛氱煭淇� */
+    public static final String CHANNEL_SMS = "SMS";
+    /** 娓犻亾锛氱珯鍐呮秷鎭� */
+    public static final String CHANNEL_SITE_MSG = "SITE_MSG";
+    /** 娓犻亾锛欰PP鎺ㄩ�� */
+    public static final String CHANNEL_APP_PUSH = "APP_PUSH";
+
+    // ==================== 鍚敤鐘舵�佸父閲� ====================
+    /** 鍚敤 */
+    public static final String ENABLED_YES = "1";
+    /** 绂佺敤 */
+    public static final String ENABLED_NO = "0";
+
+    /** 涓婚敭ID */
+    private Long id;
+
+    /** 閫氱煡绫诲瀷 */
+    @Excel(name = "閫氱煡绫诲瀷")
+    private String notifyType;
+
+    /** 娓犻亾 */
+    @Excel(name = "娓犻亾")
+    private String channel;
+
+    /** 鏄惁鍚敤 */
+    @Excel(name = "鏄惁鍚敤", readConverterExp = "0=绂佺敤,1=鍚敤")
+    private String enabled;
+
+    /** 浼樺厛绾� */
+    @Excel(name = "浼樺厛绾�")
+    private Integer priority;
+
+    /** 娓犻亾閰嶇疆(JSON鏍煎紡) */
+    private String configJson;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getNotifyType() {
+        return notifyType;
+    }
+
+    public void setNotifyType(String notifyType) {
+        this.notifyType = notifyType;
+    }
+
+    public String getChannel() {
+        return channel;
+    }
+
+    public void setChannel(String channel) {
+        this.channel = channel;
+    }
+
+    public String getEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(String enabled) {
+        this.enabled = enabled;
+    }
+
+    public Integer getPriority() {
+        return priority;
+    }
+
+    public void setPriority(Integer priority) {
+        this.priority = priority;
+    }
+
+    public String getConfigJson() {
+        return configJson;
+    }
+
+    public void setConfigJson(String configJson) {
+        this.configJson = configJson;
+    }
+
+    @Override
+    public String toString() {
+        return "NotifyChannelConfig{" +
+                "id=" + id +
+                ", notifyType='" + notifyType + '\'' +
+                ", channel='" + channel + '\'' +
+                ", enabled='" + enabled + '\'' +
+                ", priority=" + priority +
+                '}';
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/NotifySendLog.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/NotifySendLog.java
new file mode 100644
index 0000000..e14ee2d
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/NotifySendLog.java
@@ -0,0 +1,210 @@
+package com.ruoyi.system.domain;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 閫氱煡鍙戦�佽褰曞璞� sys_notify_send_log
+ * 鐢ㄤ簬璁板綍鍚勭被閫氱煡娑堟伅鐨勫彂閫佽褰曪紝瀹炵幇闃查噸鏈哄埗
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+public class NotifySendLog extends BaseEntity {
+
+    private static final long serialVersionUID = 1L;
+
+    /** 涓婚敭ID */
+    private Long id;
+
+    /** 鍏宠仈鐨勯�氱煡浠诲姟ID */
+    @Excel(name = "閫氱煡浠诲姟ID")
+    private Long notifyTaskId;
+
+    /** 浠诲姟ID */
+    @Excel(name = "浠诲姟ID")
+    private Long taskId;
+
+    /** 鎺ユ敹鐢ㄦ埛ID */
+    @Excel(name = "鐢ㄦ埛ID")
+    private Long userId;
+
+    /** 鎺ユ敹鐢ㄦ埛濮撳悕 */
+    @Excel(name = "鐢ㄦ埛濮撳悕")
+    private String userName;
+
+    /** 閫氱煡绫诲瀷锛歍ASK_ASSIGN-浠诲姟鍒嗛厤, STATUS_CHANGE-鐘舵�佸彉鏇�, TASK_CREATE-浠诲姟鍒涘缓 */
+    @Excel(name = "閫氱煡绫诲瀷")
+    private String notifyType;
+
+    /** 閫氱煡娓犻亾锛歐ECHAT-寰俊璁㈤槄娑堟伅, SMS-鐭俊, APP_PUSH-APP鎺ㄩ��, SITE_MSG-绔欏唴娑堟伅 */
+    @Excel(name = "閫氱煡娓犻亾")
+    private String channel;
+
+    /** 鍙戦�佺姸鎬侊細0-寰呭彂閫�, 1-鍙戦�佹垚鍔�, 2-鍙戦�佸け璐� */
+    @Excel(name = "鍙戦�佺姸鎬�")
+    private String sendStatus;
+
+    /** 鍙戦�佹椂闂� */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "鍙戦�佹椂闂�", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date sendTime;
+
+    /** 鍙戦�佺粨鏋�/閿欒淇℃伅 */
+    @Excel(name = "鍙戦�佺粨鏋�")
+    private String sendResult;
+
+    /** 鍝嶅簲娑堟伅 */
+    private String responseMsg;
+
+    /** 閲嶈瘯娆℃暟 */
+    @Excel(name = "閲嶈瘯娆℃暟")
+    private Integer retryCount;
+
+    // ==================== 閫氱煡绫诲瀷甯搁噺 ====================
+    /** 浠诲姟鍒嗛厤閫氱煡 */
+    public static final String NOTIFY_TYPE_TASK_ASSIGN = "TASK_ASSIGN";
+    /** 鐘舵�佸彉鏇撮�氱煡 */
+    public static final String NOTIFY_TYPE_STATUS_CHANGE = "STATUS_CHANGE";
+    /** 浠诲姟鍒涘缓閫氱煡 */
+    public static final String NOTIFY_TYPE_TASK_CREATE = "TASK_CREATE";
+
+    // ==================== 閫氱煡娓犻亾甯搁噺 ====================
+    /** 寰俊璁㈤槄娑堟伅 */
+    public static final String CHANNEL_WECHAT = "WECHAT";
+    /** 鐭俊 */
+    public static final String CHANNEL_SMS = "SMS";
+    /** APP鎺ㄩ�� */
+    public static final String CHANNEL_APP_PUSH = "APP_PUSH";
+    /** 绔欏唴娑堟伅 */
+    public static final String CHANNEL_SITE_MSG = "SITE_MSG";
+
+    // ==================== 鍙戦�佺姸鎬佸父閲� ====================
+    /** 寰呭彂閫� */
+    public static final String SEND_STATUS_PENDING = "0";
+    /** 鍙戦�佹垚鍔� */
+    public static final String SEND_STATUS_SUCCESS = "1";
+    /** 鍙戦�佸け璐� */
+    public static final String SEND_STATUS_FAILED = "2";
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getNotifyTaskId() {
+        return notifyTaskId;
+    }
+
+    public void setNotifyTaskId(Long notifyTaskId) {
+        this.notifyTaskId = notifyTaskId;
+    }
+
+    public Long getTaskId() {
+        return taskId;
+    }
+
+    public void setTaskId(Long taskId) {
+        this.taskId = taskId;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    public String getNotifyType() {
+        return notifyType;
+    }
+
+    public void setNotifyType(String notifyType) {
+        this.notifyType = notifyType;
+    }
+
+    public String getChannel() {
+        return channel;
+    }
+
+    public void setChannel(String channel) {
+        this.channel = channel;
+    }
+
+    public String getSendStatus() {
+        return sendStatus;
+    }
+
+    public void setSendStatus(String sendStatus) {
+        this.sendStatus = sendStatus;
+    }
+
+    public Date getSendTime() {
+        return sendTime;
+    }
+
+    public void setSendTime(Date sendTime) {
+        this.sendTime = sendTime;
+    }
+
+    public String getSendResult() {
+        return sendResult;
+    }
+
+    public void setSendResult(String sendResult) {
+        this.sendResult = sendResult;
+    }
+
+    public String getResponseMsg() {
+        return responseMsg;
+    }
+
+    public void setResponseMsg(String responseMsg) {
+        this.responseMsg = responseMsg;
+    }
+
+    public Integer getRetryCount() {
+        return retryCount;
+    }
+
+    public void setRetryCount(Integer retryCount) {
+        this.retryCount = retryCount;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+                .append("id", getId())
+                .append("taskId", getTaskId())
+                .append("userId", getUserId())
+                .append("userName", getUserName())
+                .append("notifyType", getNotifyType())
+                .append("channel", getChannel())
+                .append("sendStatus", getSendStatus())
+                .append("sendTime", getSendTime())
+                .append("sendResult", getSendResult())
+                .append("retryCount", getRetryCount())
+                .append("createTime", getCreateTime())
+                .append("createBy", getCreateBy())
+                .append("updateTime", getUpdateTime())
+                .append("updateBy", getUpdateBy())
+                .append("remark", getRemark())
+                .toString();
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/NotifyTask.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/NotifyTask.java
new file mode 100644
index 0000000..6f7682a
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/NotifyTask.java
@@ -0,0 +1,212 @@
+package com.ruoyi.system.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+import java.util.Date;
+
+/**
+ * 閫氱煡浠诲姟涓昏〃瀹炰綋绫�
+ * 鐢ㄤ簬缁熶竴绠$悊寰呭彂閫佺殑閫氱煡浠诲姟
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+public class NotifyTask extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    // ==================== 閫氱煡绫诲瀷甯搁噺 ====================
+    /** 閫氱煡绫诲瀷锛氫换鍔″垎閰� */
+    public static final String NOTIFY_TYPE_TASK_ASSIGN = "TASK_ASSIGN";
+    /** 閫氱煡绫诲瀷锛氱姸鎬佸彉鏇� */
+    public static final String NOTIFY_TYPE_STATUS_CHANGE = "STATUS_CHANGE";
+    /** 閫氱煡绫诲瀷锛氫换鍔″垱寤� */
+    public static final String NOTIFY_TYPE_TASK_CREATE = "TASK_CREATE";
+
+    // ==================== 澶勭悊鐘舵�佸父閲� ====================
+    /** 澶勭悊鐘舵�侊細寰呭鐞� */
+    public static final String STATUS_PENDING = "0";
+    /** 澶勭悊鐘舵�侊細澶勭悊涓� */
+    public static final String STATUS_PROCESSING = "1";
+    /** 澶勭悊鐘舵�侊細宸插畬鎴� */
+    public static final String STATUS_COMPLETED = "2";
+    /** 澶勭悊鐘舵�侊細澶辫触 */
+    public static final String STATUS_FAILED = "3";
+
+    /** 涓婚敭ID */
+    private Long id;
+
+    /** 鍏宠仈鐨勪笟鍔′换鍔D */
+    @Excel(name = "浠诲姟ID")
+    private Long taskId;
+
+    /** 浠诲姟缂栧彿 */
+    @Excel(name = "浠诲姟缂栧彿")
+    private String taskCode;
+
+    /** 閫氱煡绫诲瀷 */
+    @Excel(name = "閫氱煡绫诲瀷")
+    private String notifyType;
+
+    /** 鎺ユ敹鐢ㄦ埛ID */
+    @Excel(name = "鐢ㄦ埛ID")
+    private Long userId;
+
+    /** 鎺ユ敹鐢ㄦ埛濮撳悕 */
+    @Excel(name = "鐢ㄦ埛濮撳悕")
+    private String userName;
+
+    /** 鎺ユ敹鐢ㄦ埛鎵嬫満鍙� */
+    @Excel(name = "鎵嬫満鍙�")
+    private String userPhone;
+
+    /** 閫氱煡鏍囬 */
+    @Excel(name = "閫氱煡鏍囬")
+    private String title;
+
+    /** 閫氱煡鍐呭 */
+    @Excel(name = "閫氱煡鍐呭")
+    private String content;
+
+    /** 鎵╁睍鏁版嵁(JSON鏍煎紡) */
+    private String extraData;
+
+    /** 澶勭悊鐘舵�� */
+    @Excel(name = "澶勭悊鐘舵��", readConverterExp = "0=寰呭鐞�,1=澶勭悊涓�,2=宸插畬鎴�,3=澶辫触")
+    private String status;
+
+    /** 閲嶈瘯娆℃暟 */
+    private Integer retryCount;
+
+    /** 鏈�澶ч噸璇曟鏁� */
+    private Integer maxRetry;
+
+    /** 閿欒淇℃伅 */
+    private String errorMsg;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getTaskId() {
+        return taskId;
+    }
+
+    public void setTaskId(Long taskId) {
+        this.taskId = taskId;
+    }
+
+    public String getTaskCode() {
+        return taskCode;
+    }
+
+    public void setTaskCode(String taskCode) {
+        this.taskCode = taskCode;
+    }
+
+    public String getNotifyType() {
+        return notifyType;
+    }
+
+    public void setNotifyType(String notifyType) {
+        this.notifyType = notifyType;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    public String getUserPhone() {
+        return userPhone;
+    }
+
+    public void setUserPhone(String userPhone) {
+        this.userPhone = userPhone;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    public String getExtraData() {
+        return extraData;
+    }
+
+    public void setExtraData(String extraData) {
+        this.extraData = extraData;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public Integer getRetryCount() {
+        return retryCount;
+    }
+
+    public void setRetryCount(Integer retryCount) {
+        this.retryCount = retryCount;
+    }
+
+    public Integer getMaxRetry() {
+        return maxRetry;
+    }
+
+    public void setMaxRetry(Integer maxRetry) {
+        this.maxRetry = maxRetry;
+    }
+
+    public String getErrorMsg() {
+        return errorMsg;
+    }
+
+    public void setErrorMsg(String errorMsg) {
+        this.errorMsg = errorMsg;
+    }
+
+    @Override
+    public String toString() {
+        return "NotifyTask{" +
+                "id=" + id +
+                ", taskId=" + taskId +
+                ", taskCode='" + taskCode + '\'' +
+                ", notifyType='" + notifyType + '\'' +
+                ", userId=" + userId +
+                ", userName='" + userName + '\'' +
+                ", status='" + status + '\'' +
+                '}';
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/TaskCreateVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/TaskCreateVO.java
index abafe39..d4ec91b 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/TaskCreateVO.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/TaskCreateVO.java
@@ -123,6 +123,8 @@
 
     private Date createTime;
 
+    private  String taskStatus;
+
     // 鎵ц浜哄憳淇℃伅鍐呴儴绫�
     public static class AssigneeInfo {
         /** 鐢ㄦ埛ID */
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 c46d1d8..629bed0 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
@@ -11,6 +11,7 @@
 import com.ruoyi.system.domain.SysMessage;
 import com.ruoyi.system.domain.SysTask;
 import com.ruoyi.system.domain.SysTaskEmergency;
+import com.ruoyi.system.domain.NotifyTask;
 import com.ruoyi.system.event.TaskCreatedEvent;
 import com.ruoyi.system.event.TaskAssignedEvent;
 import com.ruoyi.system.event.TaskStatusChangedEvent;
@@ -19,9 +20,11 @@
 import com.ruoyi.system.mapper.SysTaskMapper;
 import com.ruoyi.system.mapper.SysTaskEmergencyMapper;
 import com.ruoyi.common.core.domain.entity.SysUser;
-import com.ruoyi.system.service.IWechatTaskNotifyService;
+import com.ruoyi.system.service.INotifyTaskService;
+import com.ruoyi.system.service.INotifyDispatchService;
 
-import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * 浠诲姟娑堟伅鐩戝惉鍣�
@@ -48,7 +51,15 @@
     private SysTaskEmergencyMapper sysTaskEmergencyMapper;
 
     @Autowired
-    private IWechatTaskNotifyService wechatTaskNotifyService;
+    private INotifyTaskService notifyTaskService;
+
+    @Autowired
+    private INotifyDispatchService notifyDispatchService;
+
+    /** 寰呭噯澶囩姸鎬� - 鍙互鍙戦�佺煭淇¢�氱煡 */
+    private static final String TASK_STATUS_PENDING = "PENDING";
+    /** 寰呭噯澶囩姸鎬� - 鍙互鍙戦�佺煭淇¢�氱煡 */
+    private static final String TASK_STATUS_PREPARING = "PREPARING";
 
     /**
      * 鐩戝惉浠诲姟鍒涘缓浜嬩欢
@@ -96,6 +107,7 @@
 
     /**
      * 鐩戝惉浠诲姟鍒嗛厤浜嬩欢
+     * 鍒涘缓閫氱煡浠诲姟锛岀敱閫氱煡鍒嗗彂鏈嶅姟鍐冲畾鍙戦�佹笭閬�
      * 
      * @param event 浠诲姟鍒嗛厤浜嬩欢
      */
@@ -111,10 +123,39 @@
                 log.warn("鎵ц浜篒D鍒楄〃涓虹┖锛屾棤娉曟帹閫佹秷鎭�");
                 return;
             }
+
+            // 鏌ヨ浠诲姟淇℃伅
+            SysTask task = sysTaskMapper.selectSysTaskByTaskId(event.getTaskId());
+            if (task == null) {
+                log.warn("浠诲姟涓嶅瓨鍦紝taskId={}", event.getTaskId());
+                return;
+            }
+
+            // 鏌ヨ鎬ユ晳鎵╁睍淇℃伅锛堢敤浜庢瀯寤洪�氱煡鍐呭锛�
+            SysTaskEmergency emergency = sysTaskEmergencyMapper.selectSysTaskEmergencyByTaskId(event.getTaskId());
+
+            Long creatorId = task.getCreatorId();
+            String taskStatus = task.getTaskStatus();
             
-            // 缁欐瘡涓墽琛屼汉鍙戦�佺珯鍐呮秷鎭�
-            for (int i = 0; i < event.getAssigneeIds().size(); i++) {
-                Long assigneeId = event.getAssigneeIds().get(i);
+            // 浠呭湪寰呭噯澶囩姸鎬佷笅鍙戦�侀�氱煡
+            if (!TASK_STATUS_PENDING.equals(taskStatus) && !TASK_STATUS_PREPARING.equals(taskStatus)) {
+                log.info("浠诲姟鐘舵��({})闈炲緟鍑嗗鐘舵�侊紝璺宠繃閫氱煡锛宼askId={}", taskStatus, event.getTaskId());
+                return;
+            }
+
+            // 鏋勫缓閫氱煡鍐呭
+            String notifyContent = buildNotifyContent(task, emergency);
+            
+            // 鏀堕泦鍒涘缓鐨勯�氱煡浠诲姟
+            List<NotifyTask> createdTasks = new ArrayList<>();
+            
+            // 涓烘瘡涓墽琛屼汉鍒涘缓閫氱煡浠诲姟
+            for (Long assigneeId : event.getAssigneeIds()) {
+                // 鎺掗櫎鍒涘缓浜�
+                if (creatorId != null && creatorId.equals(assigneeId)) {
+                    log.debug("璺宠繃鍒涘缓浜猴紝涓嶅彂閫佷换鍔″垎閰嶉�氱煡锛寀serId={}", assigneeId);
+                    continue;
+                }
                 
                 // 鑾峰彇鎵ц浜轰俊鎭�
                 SysUser assignee = sysUserMapper.selectUserById(assigneeId);
@@ -123,33 +164,32 @@
                     continue;
                 }
                 
-                // 鍒涘缓绔欏唴娑堟伅
-                SysMessage message = new SysMessage();
-                message.setMessageType("PUSH");
-                message.setMessageTitle("浠诲姟鎺ㄩ��");
-                message.setMessageContent("鎮ㄦ湁鏂扮殑浠诲姟锛岃鍙婃椂澶勭悊");
-                message.setTaskId(event.getTaskId());
-                message.setTaskCode(event.getTaskCode());
-                message.setReceiverId(assigneeId);
-                message.setReceiverName(assignee.getNickName());
-                message.setSenderId(event.getAssignerId());
-                message.setSenderName(event.getAssignerName() != null ? event.getAssignerName() : "绯荤粺");
-                message.setIsRead("0");
-                message.setCreateTime(DateUtils.getNowDate());
-                message.setDelFlag("0");
+                // 鍒涘缓閫氱煡浠诲姟锛堝甫闃查噸锛�
+                NotifyTask notifyTask = new NotifyTask();
+                notifyTask.setTaskId(event.getTaskId());
+                notifyTask.setTaskCode(event.getTaskCode());
+                notifyTask.setNotifyType(NotifyTask.NOTIFY_TYPE_TASK_ASSIGN);
+                notifyTask.setUserId(assigneeId);
+                notifyTask.setUserName(assignee.getNickName());
+                notifyTask.setUserPhone(assignee.getPhonenumber());
+                notifyTask.setTitle("浠诲姟鎺ㄩ��");
+                notifyTask.setContent(notifyContent);
+                notifyTask.setCreateBy(event.getAssignerName() != null ? event.getAssignerName() : "绯荤粺");
                 
-                // 淇濆瓨娑堟伅
-                sysMessageMapper.insertSysMessage(message);
-                log.info("浠诲姟鍒嗛厤娑堟伅宸蹭繚瀛橈紝娑堟伅ID锛歿}锛屾帴鏀朵汉锛歿}", message.getMessageId(), assignee.getNickName());
+                NotifyTask created = notifyTaskService.createNotifyTask(notifyTask);
+                if (created != null) {
+                    createdTasks.add(created);
+                    log.info("鍒涘缓閫氱煡浠诲姟鎴愬姛锛宨d={}, userId={}", created.getId(), assigneeId);
+                } else {
+                    log.info("閫氱煡浠诲姟宸插瓨鍦紝璺宠繃锛宼askId={}, userId={}", event.getTaskId(), assigneeId);
+                }
             }
 
-            // 鍙戦�佸井淇¤闃呮秷鎭紙鎺掗櫎鍒涘缓浜猴級
-            try {
-                SysTask task = sysTaskMapper.selectSysTaskByTaskId(event.getTaskId());
-                Long creatorId = task != null ? task.getCreatorId() : null;
-                wechatTaskNotifyService.sendTaskNotifyMessage(event.getTaskId(), event.getAssigneeIds(), creatorId);
-            } catch (Exception e) {
-                log.error("澶勭悊浠诲姟鍒嗛厤浜嬩欢鏃跺彂閫佸井淇¤闃呮秷鎭け璐�", e);
+            // 鍒嗗彂閫氱煡浠诲姟
+            if (!createdTasks.isEmpty()) {
+                int successCount = notifyDispatchService.dispatchNotifies(createdTasks);
+                log.info("閫氱煡鍒嗗彂瀹屾垚锛宼askId={}锛屽垱寤烘暟閲�={}锛屾垚鍔熸暟閲�={}", 
+                        event.getTaskId(), createdTasks.size(), successCount);
             }
             
         } catch (Exception e) {
@@ -158,6 +198,43 @@
     }
 
     /**
+     * 鏋勫缓閫氱煡鍐呭
+     */
+    private String buildNotifyContent(SysTask task, SysTaskEmergency emergency) {
+        StringBuilder content = new StringBuilder("鎮ㄦ湁鏂扮殑杞繍浠诲姟锛岃鍙婃椂澶勭悊锛屼换鍔″崟鍙�:"+task.getTaskCode());
+        
+        // 娣诲姞鍑哄彂鍦颁俊鎭�
+        String departure = null;
+        if (emergency != null && StringUtils.isNotEmpty(emergency.getHospitalOutName())) {
+            departure = emergency.getHospitalOutName();
+        } else if (StringUtils.isNotEmpty(task.getDepartureAddress())) {
+            departure = task.getDepartureAddress();
+        }
+        
+        // 娣诲姞鐩殑鍦颁俊鎭�
+        String destination = null;
+        if (emergency != null && StringUtils.isNotEmpty(emergency.getHospitalInName())) {
+            destination = emergency.getHospitalInName();
+        } else if (StringUtils.isNotEmpty(task.getDestinationAddress())) {
+            destination = task.getDestinationAddress();
+        }
+        
+        if (departure != null || destination != null) {
+            content = new StringBuilder();
+            if (departure != null) {
+                content.append("鍑哄彂鍦帮細").append(departure);
+            }
+            if (destination != null) {
+                if (content.length() > 0) content.append("锛�");
+                content.append("鐩殑鍦帮細").append(destination);
+            }
+            content.append("锛岃鍙婃椂澶勭悊銆�");
+        }
+        
+        return content.toString();
+    }
+
+    /**
      * 鐩戝惉浠诲姟鐘舵�佸彉鏇翠簨浠�
      * 
      * @param event 浠诲姟鐘舵�佸彉鏇翠簨浠�
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/LegacyTransferSyncMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/LegacyTransferSyncMapper.java
index 89fa80b..5a52599 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/LegacyTransferSyncMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/LegacyTransferSyncMapper.java
@@ -45,10 +45,10 @@
     /**
      * 鏍规嵁璋冨害鍗旾D鏌ヨ鎵ц浜轰俊鎭�
      * 
-     * @param dispatchOrdID 璋冨害鍗旾D
+     * @param dispatchOrdID 璋冨害鍗旾D锛圔IGINT绫诲瀷锛�
      * @return 鎵ц浜轰俊鎭垪琛�
      */
-    List<Map<String, Object>> selectAssigneesByDispatchOrdID(@Param("dispatchOrdID") String dispatchOrdID);
+    List<Map<String, Object>> selectAssigneesByDispatchOrdID(@Param("dispatchOrdID") Long dispatchOrdID);
     
     /**
      * 鏍规嵁杞﹁締ID鏌ヨ杞︾墝鍙�
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/NotifyChannelConfigMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/NotifyChannelConfigMapper.java
new file mode 100644
index 0000000..28e0aba
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/NotifyChannelConfigMapper.java
@@ -0,0 +1,80 @@
+package com.ruoyi.system.mapper;
+
+import com.ruoyi.system.domain.NotifyChannelConfig;
+import org.apache.ibatis.annotations.Param;
+import java.util.List;
+
+/**
+ * 閫氱煡娓犻亾閰嶇疆Mapper鎺ュ彛
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+public interface NotifyChannelConfigMapper {
+
+    /**
+     * 鏌ヨ閫氱煡娓犻亾閰嶇疆
+     * 
+     * @param id 涓婚敭ID
+     * @return 閫氱煡娓犻亾閰嶇疆
+     */
+    NotifyChannelConfig selectNotifyChannelConfigById(Long id);
+
+    /**
+     * 鏌ヨ閫氱煡娓犻亾閰嶇疆鍒楄〃
+     * 
+     * @param notifyChannelConfig 鏌ヨ鏉′欢
+     * @return 閫氱煡娓犻亾閰嶇疆鍒楄〃
+     */
+    List<NotifyChannelConfig> selectNotifyChannelConfigList(NotifyChannelConfig notifyChannelConfig);
+
+    /**
+     * 鏌ヨ鎸囧畾閫氱煡绫诲瀷鍚敤鐨勬笭閬撻厤缃�
+     * 鎸変紭鍏堢骇闄嶅簭鎺掑垪
+     * 
+     * @param notifyType 閫氱煡绫诲瀷
+     * @return 鍚敤鐨勬笭閬撻厤缃垪琛�
+     */
+    List<NotifyChannelConfig> selectEnabledChannelsByType(@Param("notifyType") String notifyType);
+
+    /**
+     * 妫�鏌ユ寚瀹氭笭閬撴槸鍚﹀惎鐢�
+     * 
+     * @param notifyType 閫氱煡绫诲瀷
+     * @param channel 娓犻亾
+     * @return 閰嶇疆淇℃伅
+     */
+    NotifyChannelConfig selectByTypeAndChannel(@Param("notifyType") String notifyType, @Param("channel") String channel);
+
+    /**
+     * 鏂板閫氱煡娓犻亾閰嶇疆
+     * 
+     * @param notifyChannelConfig 閫氱煡娓犻亾閰嶇疆
+     * @return 褰卞搷琛屾暟
+     */
+    int insertNotifyChannelConfig(NotifyChannelConfig notifyChannelConfig);
+
+    /**
+     * 淇敼閫氱煡娓犻亾閰嶇疆
+     * 
+     * @param notifyChannelConfig 閫氱煡娓犻亾閰嶇疆
+     * @return 褰卞搷琛屾暟
+     */
+    int updateNotifyChannelConfig(NotifyChannelConfig notifyChannelConfig);
+
+    /**
+     * 鍒犻櫎閫氱煡娓犻亾閰嶇疆
+     * 
+     * @param id 涓婚敭ID
+     * @return 褰卞搷琛屾暟
+     */
+    int deleteNotifyChannelConfigById(Long id);
+
+    /**
+     * 鎵归噺鍒犻櫎閫氱煡娓犻亾閰嶇疆
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑涓婚敭ID闆嗗悎
+     * @return 褰卞搷琛屾暟
+     */
+    int deleteNotifyChannelConfigByIds(Long[] ids);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/NotifySendLogMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/NotifySendLogMapper.java
new file mode 100644
index 0000000..8e040df
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/NotifySendLogMapper.java
@@ -0,0 +1,110 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+import org.apache.ibatis.annotations.Param;
+import com.ruoyi.system.domain.NotifySendLog;
+
+/**
+ * 閫氱煡鍙戦�佽褰昅apper鎺ュ彛
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+public interface NotifySendLogMapper {
+
+    /**
+     * 鏌ヨ閫氱煡鍙戦�佽褰�
+     * 
+     * @param id 閫氱煡鍙戦�佽褰曚富閿�
+     * @return 閫氱煡鍙戦�佽褰�
+     */
+    NotifySendLog selectNotifySendLogById(Long id);
+
+    /**
+     * 鏌ヨ閫氱煡鍙戦�佽褰曞垪琛�
+     * 
+     * @param notifySendLog 閫氱煡鍙戦�佽褰�
+     * @return 閫氱煡鍙戦�佽褰曢泦鍚�
+     */
+    List<NotifySendLog> selectNotifySendLogList(NotifySendLog notifySendLog);
+
+    /**
+     * 妫�鏌ユ槸鍚﹀凡瀛樺湪鍙戦�佽褰曪紙闃查噸妫�鏌ワ級
+     * 
+     * @param taskId 浠诲姟ID
+     * @param userId 鐢ㄦ埛ID
+     * @param notifyType 閫氱煡绫诲瀷
+     * @param channel 閫氱煡娓犻亾
+     * @return 璁板綍鏁伴噺
+     */
+    int checkNotifySendLogExists(@Param("taskId") Long taskId, 
+                                  @Param("userId") Long userId,
+                                  @Param("notifyType") String notifyType,
+                                  @Param("channel") String channel);
+
+    /**
+     * 鏍规嵁浠诲姟ID鍜岀敤鎴稩D鏌ヨ鍙戦�佽褰�
+     * 
+     * @param taskId 浠诲姟ID
+     * @param userId 鐢ㄦ埛ID
+     * @param notifyType 閫氱煡绫诲瀷
+     * @param channel 閫氱煡娓犻亾
+     * @return 閫氱煡鍙戦�佽褰�
+     */
+    NotifySendLog selectNotifySendLog(@Param("taskId") Long taskId,
+                                       @Param("userId") Long userId,
+                                       @Param("notifyType") String notifyType,
+                                       @Param("channel") String channel);
+
+    /**
+     * 鏂板閫氱煡鍙戦�佽褰�
+     * 
+     * @param notifySendLog 閫氱煡鍙戦�佽褰�
+     * @return 缁撴灉
+     */
+    int insertNotifySendLog(NotifySendLog notifySendLog);
+
+    /**
+     * 淇敼閫氱煡鍙戦�佽褰�
+     * 
+     * @param notifySendLog 閫氱煡鍙戦�佽褰�
+     * @return 缁撴灉
+     */
+    int updateNotifySendLog(NotifySendLog notifySendLog);
+
+    /**
+     * 鏇存柊鍙戦�佺姸鎬�
+     * 
+     * @param id 璁板綍ID
+     * @param sendStatus 鍙戦�佺姸鎬�
+     * @param sendResult 鍙戦�佺粨鏋�
+     * @return 缁撴灉
+     */
+    int updateSendStatus(@Param("id") Long id,
+                         @Param("sendStatus") String sendStatus,
+                         @Param("sendResult") String sendResult);
+
+    /**
+     * 鍒犻櫎閫氱煡鍙戦�佽褰�
+     * 
+     * @param id 閫氱煡鍙戦�佽褰曚富閿�
+     * @return 缁撴灉
+     */
+    int deleteNotifySendLogById(Long id);
+
+    /**
+     * 鎵归噺鍒犻櫎閫氱煡鍙戦�佽褰�
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑鏁版嵁涓婚敭闆嗗悎
+     * @return 缁撴灉
+     */
+    int deleteNotifySendLogByIds(Long[] ids);
+
+    /**
+     * 鏌ヨ寰呴噸璇曠殑澶辫触璁板綍
+     * 
+     * @param maxRetryCount 鏈�澶ч噸璇曟鏁�
+     * @return 澶辫触璁板綍鍒楄〃
+     */
+    List<NotifySendLog> selectFailedNotifySendLogs(@Param("maxRetryCount") Integer maxRetryCount);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/NotifyTaskMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/NotifyTaskMapper.java
new file mode 100644
index 0000000..96ecce9
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/NotifyTaskMapper.java
@@ -0,0 +1,98 @@
+package com.ruoyi.system.mapper;
+
+import com.ruoyi.system.domain.NotifyTask;
+import org.apache.ibatis.annotations.Param;
+import java.util.List;
+
+/**
+ * 閫氱煡浠诲姟Mapper鎺ュ彛
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+public interface NotifyTaskMapper {
+
+    /**
+     * 鏌ヨ閫氱煡浠诲姟
+     * 
+     * @param id 涓婚敭ID
+     * @return 閫氱煡浠诲姟
+     */
+    NotifyTask selectNotifyTaskById(Long id);
+
+    /**
+     * 鏌ヨ閫氱煡浠诲姟鍒楄〃
+     * 
+     * @param notifyTask 鏌ヨ鏉′欢
+     * @return 閫氱煡浠诲姟鍒楄〃
+     */
+    List<NotifyTask> selectNotifyTaskList(NotifyTask notifyTask);
+
+    /**
+     * 鏌ヨ寰呭鐞嗙殑閫氱煡浠诲姟
+     * 
+     * @param limit 鏌ヨ鏁伴噺闄愬埗
+     * @return 寰呭鐞嗙殑閫氱煡浠诲姟鍒楄〃
+     */
+    List<NotifyTask> selectPendingNotifyTasks(int limit);
+
+    /**
+     * 妫�鏌ラ�氱煡浠诲姟鏄惁宸插瓨鍦�
+     * 
+     * @param taskId 涓氬姟浠诲姟ID
+     * @param userId 鐢ㄦ埛ID
+     * @param notifyType 閫氱煡绫诲瀷
+     * @return 鏁伴噺
+     */
+    int countByTaskUserType(@Param("taskId") Long taskId, @Param("userId") Long userId, @Param("notifyType") String notifyType);
+
+    /**
+     * 鏂板閫氱煡浠诲姟
+     * 
+     * @param notifyTask 閫氱煡浠诲姟
+     * @return 褰卞搷琛屾暟
+     */
+    int insertNotifyTask(NotifyTask notifyTask);
+
+    /**
+     * 淇敼閫氱煡浠诲姟
+     * 
+     * @param notifyTask 閫氱煡浠诲姟
+     * @return 褰卞搷琛屾暟
+     */
+    int updateNotifyTask(NotifyTask notifyTask);
+
+    /**
+     * 鏇存柊閫氱煡浠诲姟鐘舵��
+     * 
+     * @param id 涓婚敭ID
+     * @param status 鐘舵��
+     * @param errorMsg 閿欒淇℃伅
+     * @return 褰卞搷琛屾暟
+     */
+    int updateNotifyTaskStatus(@Param("id") Long id, @Param("status") String status, @Param("errorMsg") String errorMsg);
+
+    /**
+     * 澧炲姞閲嶈瘯娆℃暟
+     * 
+     * @param id 涓婚敭ID
+     * @return 褰卞搷琛屾暟
+     */
+    int incrementRetryCount(Long id);
+
+    /**
+     * 鍒犻櫎閫氱煡浠诲姟
+     * 
+     * @param id 涓婚敭ID
+     * @return 褰卞搷琛屾暟
+     */
+    int deleteNotifyTaskById(Long id);
+
+    /**
+     * 鎵归噺鍒犻櫎閫氱煡浠诲姟
+     * 
+     * @param ids 涓婚敭ID鏁扮粍
+     * @return 褰卞搷琛屾暟
+     */
+    int deleteNotifyTaskByIds(Long[] ids);
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTaskAssigneeMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTaskAssigneeMapper.java
index 58b67bd..808db11 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTaskAssigneeMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTaskAssigneeMapper.java
@@ -1,6 +1,7 @@
 package com.ruoyi.system.mapper;
 
 import com.ruoyi.system.domain.SysTaskAssignee;
+import org.apache.ibatis.annotations.Param;
 import java.util.List;
 
 /**
@@ -74,4 +75,13 @@
      * @return 缁撴灉
      */
     public int deleteSysTaskAssigneeByIds(Long[] ids);
+
+    /**
+     * 鏍规嵁浠诲姟ID鍜岀敤鎴稩D鍒犻櫎鎵ц浜哄憳鍏宠仈
+     * 
+     * @param taskId 浠诲姟ID
+     * @param userId 鐢ㄦ埛ID
+     * @return 缁撴灉
+     */
+    public int deleteByTaskIdAndUserId(@Param("taskId") Long taskId, @Param("userId") Long userId);
 }
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 deeda59..c918380 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
@@ -1,5 +1,6 @@
 package com.ruoyi.system.mapper;
 
+import java.util.Date;
 import java.util.List;
 
 import com.ruoyi.common.annotation.DataSource;
@@ -7,6 +8,7 @@
 import com.ruoyi.system.domain.SysTask;
 import com.ruoyi.system.domain.vo.TaskQueryVO;
 import com.ruoyi.system.domain.vo.TaskStatisticsVO;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * 浠诲姟绠$悊Mapper鎺ュ彛
@@ -127,4 +129,15 @@
      * @return 姝e湪杩涜涓殑浠诲姟鍒楄〃
      */
     public List<SysTask> selectActiveTasksByVehicleId(Long vehicleId);
+
+    /**
+     * 鏍规嵁鏃堕棿鍜岃溅杈嗘潵鏌ヨ璇ヨ溅杈嗘槸鍚︽鍦ㄤ换鍔′腑
+     * @param vehicleId
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    public List<SysTask> selectTaskByVehicleIdAndDate(@Param("vehicleId") Long vehicleId,
+                                                      @Param("startTime") Date startTime,
+                                                      @Param("endTime") Date endTime);
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleMileageStatsMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleMileageStatsMapper.java
index 328dbe8..1740365 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleMileageStatsMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleMileageStatsMapper.java
@@ -71,7 +71,7 @@
 
     /**
      * 鏌ヨ杞﹁締鍦ㄦ寚瀹氭椂闂磋寖鍥村唴鐨勪换鍔℃椂闂村尯闂�
-     * 
+     *
      * @param vehicleId 杞﹁締ID
      * @param startTime 寮�濮嬫椂闂�
      * @param endTime 缁撴潫鏃堕棿
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/INotifyChannelConfigService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/INotifyChannelConfigService.java
new file mode 100644
index 0000000..e82e459
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/INotifyChannelConfigService.java
@@ -0,0 +1,70 @@
+package com.ruoyi.system.service;
+
+import com.ruoyi.system.domain.NotifyChannelConfig;
+import java.util.List;
+
+/**
+ * 閫氱煡娓犻亾閰嶇疆Service鎺ュ彛
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+public interface INotifyChannelConfigService {
+    /**
+     * 鏌ヨ閫氱煡娓犻亾閰嶇疆
+     * 
+     * @param id 涓婚敭ID
+     * @return 閫氱煡娓犻亾閰嶇疆
+     */
+    public NotifyChannelConfig selectNotifyChannelConfigById(Long id);
+
+    /**
+     * 鏌ヨ閫氱煡娓犻亾閰嶇疆鍒楄〃
+     * 
+     * @param notifyChannelConfig 鏌ヨ鏉′欢
+     * @return 閫氱煡娓犻亾閰嶇疆鍒楄〃
+     */
+    public List<NotifyChannelConfig> selectNotifyChannelConfigList(NotifyChannelConfig notifyChannelConfig);
+
+    /**
+     * 鏌ヨ鎸囧畾閫氱煡绫诲瀷鍚敤鐨勬笭閬撻厤缃�
+     * 鎸変紭鍏堢骇闄嶅簭鎺掑垪
+     * 
+     * @param notifyType 閫氱煡绫诲瀷
+     * @return 鍚敤鐨勬笭閬撻厤缃垪琛�
+     */
+    public List<NotifyChannelConfig> selectEnabledChannelsByType(String notifyType);
+
+    /**
+     * 妫�鏌ユ寚瀹氭笭閬撴槸鍚﹀惎鐢�
+     * 
+     * @param notifyType 閫氱煡绫诲瀷
+     * @param channel 娓犻亾
+     * @return 閰嶇疆淇℃伅
+     */
+    public NotifyChannelConfig selectByTypeAndChannel(String notifyType, String channel);
+
+    /**
+     * 鏂板閫氱煡娓犻亾閰嶇疆
+     * 
+     * @param notifyChannelConfig 閫氱煡娓犻亾閰嶇疆
+     * @return 缁撴灉
+     */
+    public int insertNotifyChannelConfig(NotifyChannelConfig notifyChannelConfig);
+
+    /**
+     * 淇敼閫氱煡娓犻亾閰嶇疆
+     * 
+     * @param notifyChannelConfig 閫氱煡娓犻亾閰嶇疆
+     * @return 缁撴灉
+     */
+    public int updateNotifyChannelConfig(NotifyChannelConfig notifyChannelConfig);
+
+    /**
+     * 鎵归噺鍒犻櫎閫氱煡娓犻亾閰嶇疆
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑涓婚敭ID闆嗗悎
+     * @return 缁撴灉
+     */
+    public int deleteNotifyChannelConfigByIds(Long[] ids);
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/INotifyDispatchService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/INotifyDispatchService.java
new file mode 100644
index 0000000..b2ca905
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/INotifyDispatchService.java
@@ -0,0 +1,81 @@
+package com.ruoyi.system.service;
+
+import com.ruoyi.system.domain.NotifyTask;
+import com.ruoyi.system.domain.NotifyChannelConfig;
+import java.util.List;
+
+/**
+ * 閫氱煡鍒嗗彂鏈嶅姟鎺ュ彛
+ * 璐熻矗鏍规嵁閰嶇疆鍐冲畾鍙戦�佹笭閬撳苟鎵ц鍒嗗彂
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+public interface INotifyDispatchService {
+
+    /**
+     * 鑾峰彇鎸囧畾閫氱煡绫诲瀷鍚敤鐨勬笭閬撳垪琛�
+     * 
+     * @param notifyType 閫氱煡绫诲瀷
+     * @return 鍚敤鐨勬笭閬撻厤缃垪琛紙鎸変紭鍏堢骇鎺掑簭锛�
+     */
+    List<NotifyChannelConfig> getEnabledChannels(String notifyType);
+
+    /**
+     * 妫�鏌ユ寚瀹氭笭閬撴槸鍚﹀惎鐢�
+     * 
+     * @param notifyType 閫氱煡绫诲瀷
+     * @param channel 娓犻亾
+     * @return true=鍚敤, false=鏈惎鐢�
+     */
+    boolean isChannelEnabled(String notifyType, String channel);
+
+    /**
+     * 鍒嗗彂閫氱煡浠诲姟
+     * 鏍规嵁娓犻亾閰嶇疆锛屽悜鍚勫惎鐢ㄧ殑娓犻亾鍙戦�侀�氱煡
+     * 
+     * @param notifyTask 閫氱煡浠诲姟
+     * @return 鎴愬姛鍙戦�佺殑娓犻亾鏁伴噺
+     */
+    int dispatchNotify(NotifyTask notifyTask);
+
+    /**
+     * 鎵归噺鍒嗗彂閫氱煡浠诲姟
+     * 
+     * @param notifyTasks 閫氱煡浠诲姟鍒楄〃
+     * @return 鎴愬姛鍒嗗彂鐨勪换鍔℃暟閲�
+     */
+    int dispatchNotifies(List<NotifyTask> notifyTasks);
+
+    /**
+     * 澶勭悊寰呭彂閫佺殑閫氱煡浠诲姟锛堝彲鐢ㄤ簬瀹氭椂浠诲姟璋冪敤锛�
+     * 
+     * @param limit 姣忔澶勭悊鐨勬暟閲忛檺鍒�
+     * @return 澶勭悊鐨勪换鍔℃暟閲�
+     */
+    int processPendingNotifies(int limit);
+
+    /**
+     * 鍙戦�佺珯鍐呮秷鎭�
+     * 
+     * @param notifyTask 閫氱煡浠诲姟
+     * @return 鏄惁鍙戦�佹垚鍔�
+     */
+    boolean sendSiteMessage(NotifyTask notifyTask);
+
+    /**
+     * 鍙戦�佸井淇¤闃呮秷鎭�
+     * 
+     * @param notifyTask 閫氱煡浠诲姟
+     * @return 鏄惁鍙戦�佹垚鍔�
+     */
+    boolean sendWechatMessage(NotifyTask notifyTask);
+
+    /**
+     * 鍙戦�佺煭淇℃秷鎭�
+     * 
+     * @param notifyTask 閫氱煡浠诲姟
+     * @return 鏄惁鍙戦�佹垚鍔�
+     */
+    boolean sendSmsMessage(NotifyTask notifyTask);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/INotifySendLogService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/INotifySendLogService.java
new file mode 100644
index 0000000..a6385ad
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/INotifySendLogService.java
@@ -0,0 +1,109 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import com.ruoyi.system.domain.NotifySendLog;
+
+/**
+ * 閫氱煡鍙戦�佽褰曟湇鍔℃帴鍙�
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+public interface INotifySendLogService {
+
+    /**
+     * 鏌ヨ閫氱煡鍙戦�佽褰�
+     * 
+     * @param id 閫氱煡鍙戦�佽褰曚富閿�
+     * @return 閫氱煡鍙戦�佽褰�
+     */
+    NotifySendLog selectNotifySendLogById(Long id);
+
+    /**
+     * 鏌ヨ閫氱煡鍙戦�佽褰曞垪琛�
+     * 
+     * @param notifySendLog 閫氱煡鍙戦�佽褰�
+     * @return 閫氱煡鍙戦�佽褰曢泦鍚�
+     */
+    List<NotifySendLog> selectNotifySendLogList(NotifySendLog notifySendLog);
+
+    /**
+     * 妫�鏌ユ槸鍚﹀凡鍙戦�佽繃閫氱煡锛堥槻閲嶆鏌ワ級
+     * 
+     * @param taskId 浠诲姟ID
+     * @param userId 鐢ㄦ埛ID
+     * @param notifyType 閫氱煡绫诲瀷
+     * @param channel 閫氱煡娓犻亾
+     * @return true=宸插彂閫佽繃, false=鏈彂閫佽繃
+     */
+    boolean hasNotified(Long taskId, Long userId, String notifyType, String channel);
+
+    /**
+     * 灏濊瘯鍒涘缓鍙戦�佽褰曪紙闃查噸锛屽鏋滃凡瀛樺湪鍒欒繑鍥瀎alse锛�
+     * 
+     * @param taskId 浠诲姟ID
+     * @param userId 鐢ㄦ埛ID
+     * @param userName 鐢ㄦ埛濮撳悕
+     * @param notifyType 閫氱煡绫诲瀷
+     * @param channel 閫氱煡娓犻亾
+     * @return 鍒涘缓鐨勮褰曪紝濡傛灉宸插瓨鍦ㄥ垯杩斿洖null
+     */
+    NotifySendLog tryCreateSendLog(Long taskId, Long userId, String userName, 
+                                    String notifyType, String channel);
+
+    /**
+     * 鏇存柊鍙戦�佺姸鎬佷负鎴愬姛
+     * 
+     * @param id 璁板綍ID
+     * @param result 鍙戦�佺粨鏋滀俊鎭�
+     */
+    void markSendSuccess(Long id, String result);
+
+    /**
+     * 鏇存柊鍙戦�佺姸鎬佷负澶辫触
+     * 
+     * @param id 璁板綍ID
+     * @param errorMsg 閿欒淇℃伅
+     */
+    void markSendFailed(Long id, String errorMsg);
+
+    /**
+     * 鏂板閫氱煡鍙戦�佽褰�
+     * 
+     * @param notifySendLog 閫氱煡鍙戦�佽褰�
+     * @return 缁撴灉
+     */
+    int insertNotifySendLog(NotifySendLog notifySendLog);
+
+    /**
+     * 淇敼閫氱煡鍙戦�佽褰�
+     * 
+     * @param notifySendLog 閫氱煡鍙戦�佽褰�
+     * @return 缁撴灉
+     */
+    int updateNotifySendLog(NotifySendLog notifySendLog);
+
+    /**
+     * 鎵归噺鍒犻櫎閫氱煡鍙戦�佽褰�
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑閫氱煡鍙戦�佽褰曚富閿泦鍚�
+     * @return 缁撴灉
+     */
+    int deleteNotifySendLogByIds(Long[] ids);
+
+    /**
+     * 鍒犻櫎閫氱煡鍙戦�佽褰曚俊鎭�
+     * 
+     * @param id 閫氱煡鍙戦�佽褰曚富閿�
+     * @return 缁撴灉
+     */
+    int deleteNotifySendLogById(Long id);
+
+    /**
+     * 鏌ヨ寰呴噸璇曠殑澶辫触璁板綍
+     * 
+     * @param maxRetryCount 鏈�澶ч噸璇曟鏁�
+     * @return 澶辫触璁板綍鍒楄〃
+     */
+    List<NotifySendLog> selectFailedNotifySendLogs(Integer maxRetryCount);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/INotifyTaskService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/INotifyTaskService.java
new file mode 100644
index 0000000..eeaa253
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/INotifyTaskService.java
@@ -0,0 +1,121 @@
+package com.ruoyi.system.service;
+
+import com.ruoyi.system.domain.NotifyTask;
+import java.util.List;
+
+/**
+ * 閫氱煡浠诲姟鏈嶅姟鎺ュ彛
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+public interface INotifyTaskService {
+
+    /**
+     * 鏌ヨ閫氱煡浠诲姟
+     * 
+     * @param id 涓婚敭ID
+     * @return 閫氱煡浠诲姟
+     */
+    NotifyTask selectNotifyTaskById(Long id);
+
+    /**
+     * 鏌ヨ閫氱煡浠诲姟鍒楄〃
+     * 
+     * @param notifyTask 鏌ヨ鏉′欢
+     * @return 閫氱煡浠诲姟鍒楄〃
+     */
+    List<NotifyTask> selectNotifyTaskList(NotifyTask notifyTask);
+
+    /**
+     * 鏌ヨ寰呭鐞嗙殑閫氱煡浠诲姟
+     * 
+     * @param limit 鏌ヨ鏁伴噺闄愬埗
+     * @return 寰呭鐞嗙殑閫氱煡浠诲姟鍒楄〃
+     */
+    List<NotifyTask> selectPendingNotifyTasks(int limit);
+
+    /**
+     * 妫�鏌ラ�氱煡浠诲姟鏄惁宸插瓨鍦紙闃查噸锛�
+     * 
+     * @param taskId 涓氬姟浠诲姟ID
+     * @param userId 鐢ㄦ埛ID
+     * @param notifyType 閫氱煡绫诲瀷
+     * @return true=宸插瓨鍦�, false=涓嶅瓨鍦�
+     */
+    boolean existsNotifyTask(Long taskId, Long userId, String notifyType);
+
+    /**
+     * 鍒涘缓閫氱煡浠诲姟锛堝甫闃查噸锛�
+     * 濡傛灉宸插瓨鍦ㄥ垯杩斿洖null锛屽惁鍒欏垱寤哄苟杩斿洖
+     * 
+     * @param notifyTask 閫氱煡浠诲姟
+     * @return 鍒涘缓鐨勯�氱煡浠诲姟锛屽鏋滃凡瀛樺湪杩斿洖null
+     */
+    NotifyTask createNotifyTask(NotifyTask notifyTask);
+
+    /**
+     * 鎵归噺鍒涘缓閫氱煡浠诲姟
+     * 
+     * @param notifyTasks 閫氱煡浠诲姟鍒楄〃
+     * @return 鎴愬姛鍒涘缓鐨勬暟閲�
+     */
+    int createNotifyTasks(List<NotifyTask> notifyTasks);
+
+    /**
+     * 淇敼閫氱煡浠诲姟
+     * 
+     * @param notifyTask 閫氱煡浠诲姟
+     * @return 褰卞搷琛屾暟
+     */
+    int updateNotifyTask(NotifyTask notifyTask);
+
+    /**
+     * 鏇存柊閫氱煡浠诲姟鐘舵�佷负澶勭悊涓�
+     * 
+     * @param id 涓婚敭ID
+     * @return 褰卞搷琛屾暟
+     */
+    int markProcessing(Long id);
+
+    /**
+     * 鏇存柊閫氱煡浠诲姟鐘舵�佷负瀹屾垚
+     * 
+     * @param id 涓婚敭ID
+     * @return 褰卞搷琛屾暟
+     */
+    int markCompleted(Long id);
+
+    /**
+     * 鏇存柊閫氱煡浠诲姟鐘舵�佷负澶辫触
+     * 
+     * @param id 涓婚敭ID
+     * @param errorMsg 閿欒淇℃伅
+     * @return 褰卞搷琛屾暟
+     */
+    int markFailed(Long id, String errorMsg);
+
+    /**
+     * 澧炲姞閲嶈瘯娆℃暟
+     * 
+     * @param id 涓婚敭ID
+     * @return 褰卞搷琛屾暟
+     */
+    int incrementRetryCount(Long id);
+
+    /**
+     * 鍒犻櫎閫氱煡浠诲姟
+     * 
+     * @param id 涓婚敭ID
+     * @return 褰卞搷琛屾暟
+     */
+    int deleteNotifyTaskById(Long id);
+
+    /**
+     * 鎵归噺鍒犻櫎閫氱煡浠诲姟
+     * 
+     * @param ids 涓婚敭ID鏁扮粍
+     * @return 褰卞搷琛屾暟
+     */
+    int deleteNotifyTaskByIds(Long[] ids);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISmsService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISmsService.java
new file mode 100644
index 0000000..59e7110
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISmsService.java
@@ -0,0 +1,64 @@
+package com.ruoyi.system.service;
+
+import com.ruoyi.system.domain.SysTask;
+import java.util.List;
+
+/**
+ * 鐭俊鍙戦�佹湇鍔℃帴鍙�
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+public interface ISmsService {
+
+    /**
+     * 妫�鏌ョ煭淇℃湇鍔℃槸鍚﹀惎鐢�
+     * 
+     * @return true=鍚敤, false=绂佺敤
+     */
+    boolean isEnabled();
+
+    /**
+     * 鍙戦�佺煭淇�
+     * 
+     * @param phone 鎵嬫満鍙�
+     * @param content 鐭俊鍐呭
+     * @return 鏄惁鍙戦�佹垚鍔�
+     */
+    boolean sendSms(String phone, String content);
+
+    /**
+     * 鎵归噺鍙戦�佺煭淇�
+     * 
+     * @param phones 鎵嬫満鍙峰垪琛�
+     * @param content 鐭俊鍐呭
+     * @return 鎴愬姛鍙戦�佺殑鏁伴噺
+     */
+    int sendSmsBatch(List<String> phones, String content);
+
+    /**
+     * 鍙戦�佷换鍔″垎閰嶉�氱煡鐭俊
+     * 
+     * @param taskId 浠诲姟ID
+     * @param userIds 鐢ㄦ埛ID鍒楄〃
+     * @param excludeUserId 鎺掗櫎鐨勭敤鎴稩D锛堝彲閫夛紝濡傚垱寤轰汉锛�
+     * @return 鎴愬姛鍙戦�佺殑鏁伴噺
+     */
+    int sendTaskAssignSms(Long taskId, List<Long> userIds, Long excludeUserId);
+
+    /**
+     * 鍙戦�佷换鍔″垎閰嶉�氱煡鐭俊锛堜笉鎺掗櫎浠讳綍鐢ㄦ埛锛�
+     * 
+     * @param taskId 浠诲姟ID
+     * @param userIds 鐢ㄦ埛ID鍒楄〃
+     * @return 鎴愬姛鍙戦�佺殑鏁伴噺
+     */
+    int sendTaskAssignSms(Long taskId, List<Long> userIds);
+
+    /**
+     * 鏌ヨ鐭俊浣欓
+     * 
+     * @return 浣欓淇℃伅
+     */
+    String getBalance();
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysEmergencyTaskService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysEmergencyTaskService.java
new file mode 100644
index 0000000..3768c4e
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysEmergencyTaskService.java
@@ -0,0 +1,33 @@
+package com.ruoyi.system.service;
+
+import com.ruoyi.system.domain.SysTaskEmergency;
+import com.ruoyi.system.domain.SysTask;
+import com.ruoyi.system.domain.vo.TaskCreateVO;
+import com.ruoyi.system.domain.vo.TaskUpdateVO;
+
+public interface ISysEmergencyTaskService {
+
+    void saveEmergencyInfo(Long taskId, String createUserName, TaskCreateVO createVO,
+                           String serviceOrderId, String dispatchOrderId, String serviceOrdNo);
+
+    void updateEmergencyInfoFromUpdateVO(SysTaskEmergency oldEmergency, TaskUpdateVO updateVO, String userName);
+
+    /**
+     * 浠� TaskCreateVO 鏇存柊鎬ユ晳杞繍浠诲姟鎵╁睍淇℃伅锛堢敤浜庢棫绯荤粺鍚屾锛�
+     * 
+     * @param existingInfo 鐜版湁鐨勬�ユ晳浠诲姟淇℃伅
+     * @param createVO 浠诲姟鍒涘缓瀵硅薄
+     * @param userName 鎿嶄綔浜虹敤鎴峰悕
+     */
+    void updateEmergencyInfoFromCreateVO(SysTaskEmergency existingInfo, TaskCreateVO createVO, String userName);
+
+    void markNeedResyncIfNecessary(Long taskId, SysTask oldTask, TaskUpdateVO updateVO, Boolean updateFromLegacy);
+
+    boolean hasLegacyServiceOrdId(Long taskId);
+
+    boolean hasLegacyDispatchOrdId(Long taskId);
+
+    boolean existsByLegacyServiceOrdId(Long legacyServiceOrdId);
+
+    boolean existsByLegacyDispatchOrdId(Long legacyDispatchOrdId);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskAssigneeService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskAssigneeService.java
new file mode 100644
index 0000000..016bf8c
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskAssigneeService.java
@@ -0,0 +1,84 @@
+package com.ruoyi.system.service;
+
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.system.domain.SysTaskAssignee;
+import com.ruoyi.system.domain.vo.TaskCreateVO;
+
+import java.util.List;
+
+/**
+ * 浠诲姟鎵ц浜烘湇鍔℃帴鍙�
+ * 
+ * @author ruoyi
+ */
+public interface ISysTaskAssigneeService {
+
+    /**
+     * 淇濆瓨浠诲姟鎵ц浜哄憳淇℃伅
+     * 
+     * @param taskId 浠诲姟ID
+     * @param assignees 鎵ц浜哄憳淇℃伅鍒楄〃
+     * @param userName 鎿嶄綔浜�
+     */
+    void saveTaskAssignees(Long taskId, List<TaskCreateVO.AssigneeInfo> assignees, String userName);
+
+    /**
+     * 鏇存柊浠诲姟鎵ц浜哄憳锛堝惈鍙樻洿妫�娴嬶紝浠呭湪鏈夊彉鍖栨椂鎵嶆洿鏂帮級
+     * 
+     * @param taskId 浠诲姟ID
+     * @param newAssignees 鏂扮殑鎵ц浜哄憳淇℃伅鍒楄〃
+     * @param userName 鎿嶄綔浜�
+     * @return 鏄惁鏈夊彉鏇�
+     */
+    boolean updateTaskAssignees(Long taskId, List<TaskCreateVO.AssigneeInfo> newAssignees, String userName);
+
+    /**
+     * 鏍规嵁浠诲姟ID鑾峰彇鎵ц浜哄垪琛�
+     * 
+     * @param taskId 浠诲姟ID
+     * @return 鎵ц浜哄垪琛�
+     */
+    List<SysTaskAssignee> getAssigneesByTaskId(Long taskId);
+
+    /**
+     * 鍒犻櫎浠诲姟鐨勬墍鏈夋墽琛屼汉
+     * 
+     * @param taskId 浠诲姟ID
+     * @return 鍒犻櫎鏁伴噺
+     */
+    int deleteAssigneesByTaskId(Long taskId);
+
+    /**
+     * 鎵ц浜虹偣鍑诲氨缁�
+     * 
+     * @param taskId 浠诲姟ID
+     * @param userId 鐢ㄦ埛ID
+     * @return 缁撴灉
+     */
+    AjaxResult setAssigneeReady(Long taskId, Long userId);
+
+    /**
+     * 鍙栨秷鎵ц浜哄氨缁�
+     * 
+     * @param taskId 浠诲姟ID
+     * @param userId 鐢ㄦ埛ID
+     * @return 缁撴灉
+     */
+    AjaxResult cancelAssigneeReady(Long taskId, Long userId);
+
+    /**
+     * 鑾峰彇鎵ц浜篒D鍒楄〃
+     * 
+     * @param taskId 浠诲姟ID
+     * @return 鎵ц浜篒D鍒楄〃
+     */
+    List<Long> getAssigneeIds(Long taskId);
+
+    /**
+     * 鑾峰彇鎵ц浜哄鍚嶅垪琛�
+     * 
+     * @param taskId 浠诲姟ID
+     * @return 鎵ц浜哄鍚嶅垪琛�
+     */
+    List<String> getAssigneeNames(Long taskId);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskAttachmentService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskAttachmentService.java
new file mode 100644
index 0000000..6f9cd58
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskAttachmentService.java
@@ -0,0 +1,33 @@
+package com.ruoyi.system.service;
+
+import com.ruoyi.system.domain.SysTaskAttachment;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+
+public interface ISysTaskAttachmentService {
+    Long uploadAttachment(Long taskId, MultipartFile file, String category);
+
+    Long uploadAttachmentFromWechat(Long taskId, String accessToken, String mediaId, String category);
+
+    int deleteAttachment(Long attachmentId);
+
+    SysTaskAttachment getAttachmentById(Long attachmentId);
+
+    List<SysTaskAttachment> getAttachmentsByTaskId(Long taskId);
+
+    /**
+     * 鑾峰彇闄勪欢鍒嗙被鎻忚堪
+     * 
+     * @param category 闄勪欢鍒嗙被浠g爜
+     * @return 鍒嗙被鎻忚堪
+     */
+    String getCategoryDesc(String category);
+
+    /**
+     * 鏋勫缓闄勪欢鐨勫畬鏁碪RL
+     * 
+     * @param attachment 闄勪欢瀵硅薄
+     */
+    void buildAttachmentUrl(SysTaskAttachment attachment);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskVehicleService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskVehicleService.java
index 0e1ec8b..98f0e82 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskVehicleService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskVehicleService.java
@@ -1,24 +1,17 @@
 package com.ruoyi.system.service;
 
-import java.util.List;
-import java.util.Map;
 import com.ruoyi.system.domain.SysTaskVehicle;
 
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
 /**
- * 浠诲姟杞﹁締鍏宠仈Service鎺ュ彛
+ * 浠诲姟杞﹁締鏈嶅姟鎺ュ彛
  * 
  * @author ruoyi
- * @date 2024-01-15
  */
 public interface ISysTaskVehicleService {
-    
-    /**
-     * 鏌ヨ浠诲姟杞﹁締鍏宠仈
-     * 
-     * @param id 浠诲姟杞﹁締鍏宠仈涓婚敭
-     * @return 浠诲姟杞﹁締鍏宠仈
-     */
-    public SysTaskVehicle selectSysTaskVehicleById(Long id);
 
     /**
      * 鏌ヨ浠诲姟杞﹁締鍏宠仈鍒楄〃
@@ -26,7 +19,15 @@
      * @param sysTaskVehicle 浠诲姟杞﹁締鍏宠仈
      * @return 浠诲姟杞﹁締鍏宠仈闆嗗悎
      */
-    public List<SysTaskVehicle> selectSysTaskVehicleList(SysTaskVehicle sysTaskVehicle);
+    List<SysTaskVehicle> selectSysTaskVehicleList(SysTaskVehicle sysTaskVehicle);
+
+    /**
+     * 鏌ヨ浠诲姟杞﹁締鍏宠仈淇℃伅
+     * 
+     * @param id 浠诲姟杞﹁締鍏宠仈涓婚敭
+     * @return 浠诲姟杞﹁締鍏宠仈淇℃伅
+     */
+    SysTaskVehicle selectSysTaskVehicleById(Long id);
 
     /**
      * 鏍规嵁浠诲姟ID鏌ヨ鍏宠仈杞﹁締鍒楄〃
@@ -34,7 +35,7 @@
      * @param taskId 浠诲姟ID
      * @return 浠诲姟杞﹁締鍏宠仈闆嗗悎
      */
-    public List<SysTaskVehicle> selectSysTaskVehicleByTaskId(Long taskId);
+    List<SysTaskVehicle> selectSysTaskVehicleByTaskId(Long taskId);
 
     /**
      * 鏂板浠诲姟杞﹁締鍏宠仈
@@ -42,7 +43,7 @@
      * @param sysTaskVehicle 浠诲姟杞﹁締鍏宠仈
      * @return 缁撴灉
      */
-    public int insertSysTaskVehicle(SysTaskVehicle sysTaskVehicle);
+    int insertSysTaskVehicle(SysTaskVehicle sysTaskVehicle);
 
     /**
      * 淇敼浠诲姟杞﹁締鍏宠仈
@@ -50,15 +51,7 @@
      * @param sysTaskVehicle 浠诲姟杞﹁締鍏宠仈
      * @return 缁撴灉
      */
-    public int updateSysTaskVehicle(SysTaskVehicle sysTaskVehicle);
-
-    /**
-     * 鎵归噺鍒犻櫎浠诲姟杞﹁締鍏宠仈
-     * 
-     * @param ids 闇�瑕佸垹闄ょ殑浠诲姟杞﹁締鍏宠仈涓婚敭闆嗗悎
-     * @return 缁撴灉
-     */
-    public int deleteSysTaskVehicleByIds(Long[] ids);
+    int updateSysTaskVehicle(SysTaskVehicle sysTaskVehicle);
 
     /**
      * 鍒犻櫎浠诲姟杞﹁締鍏宠仈淇℃伅
@@ -66,51 +59,73 @@
      * @param id 浠诲姟杞﹁締鍏宠仈涓婚敭
      * @return 缁撴灉
      */
-    public int deleteSysTaskVehicleById(Long id);
+    int deleteSysTaskVehicleById(Long id);
 
     /**
-     * 鏍规嵁浠诲姟ID鍒犻櫎杞﹁締鍏宠仈
+     * 鎵归噺鍒犻櫎浠诲姟杞﹁締鍏宠仈淇℃伅
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑涓婚敭闆嗗悎
+     * @return 缁撴灉
+     */
+    int deleteSysTaskVehicleByIds(Long[] ids);
+
+    /**
+     * 鏇存柊浠诲姟杞﹁締鍏宠仈鐘舵��
+     * 
+     * @param id 浠诲姟杞﹁締鍏宠仈涓婚敭
+     * @param status 鐘舵��
+     * @return 缁撴灉
+     */
+    int updateSysTaskVehicleStatus(Long id, String status);
+
+    /**
+     * 鎵归噺鑾峰彇杞﹁締褰撳墠浠诲姟鐘舵��
+     * 
+     * @param vehicleIds 杞﹁締ID鍒楄〃
+     * @return 杞﹁締浠诲姟鐘舵�佹槧灏�
+     */
+    Map<Long, Map<String, Object>> batchGetVehicleCurrentTaskStatus(List<Long> vehicleIds);
+
+    /**
+     * 淇濆瓨浠诲姟杞﹁締鍏宠仈
      * 
      * @param taskId 浠诲姟ID
-     * @return 缁撴灉
+     * @param vehicleIds 杞﹁締ID鍒楄〃
+     * @param userName 鎿嶄綔浜虹敤鎴峰悕
      */
-    public int deleteSysTaskVehicleByTaskId(Long taskId);
+    void saveTaskVehicles(Long taskId, List<Long> vehicleIds, String userName);
 
     /**
-     * 鏍规嵁浠诲姟ID鍜岃溅杈咺D鍒犻櫎鍏宠仈
+     * 淇濆瓨浠诲姟杞﹁締鍏宠仈锛堟寚瀹氭椂闂达級
      * 
      * @param taskId 浠诲姟ID
-     * @param vehicleId 杞﹁締ID
-     * @return 缁撴灉
+     * @param vehicleIds 杞﹁締ID鍒楄〃
+     * @param userName 鎿嶄綔浜虹敤鎴峰悕
+     * @param assignTime 鍒嗛厤鏃堕棿
+     * @param createTime 鍒涘缓鏃堕棿
+     * @param updateTime 鏇存柊鏃堕棿
      */
-    public int deleteSysTaskVehicleByTaskIdAndVehicleId(Long taskId, Long vehicleId);
+    void saveTaskVehicles(Long taskId, List<Long> vehicleIds, String userName, 
+                          Date assignTime, Date createTime, Date updateTime);
 
     /**
-     * 妫�鏌ヤ换鍔¤溅杈嗗叧鑱旀槸鍚﹀瓨鍦�
+     * 鏇存柊浠诲姟杞﹁締鍏宠仈
      * 
      * @param taskId 浠诲姟ID
-     * @param vehicleId 杞﹁締ID
-     * @return 缁撴灉
+     * @param newVehicleIds 鏂扮殑杞﹁締ID鍒楄〃
+     * @param userName 鎿嶄綔浜虹敤鎴峰悕
+     * @return 鏄惁鏈夊彉鏇�
      */
-    public int checkTaskVehicleExists(Long taskId, Long vehicleId);
-
-    /**
-     * 鎵归噺鏂板浠诲姟杞﹁締鍏宠仈
-     * 
-     * @param sysTaskVehicleList 浠诲姟杞﹁締鍏宠仈鍒楄〃
-     * @return 缁撴灉
-     */
-    public int batchInsertSysTaskVehicle(List<SysTaskVehicle> sysTaskVehicleList);
-
-    /**
-     * 鍒嗛厤杞﹁締缁欎换鍔�
-     * 
+    boolean updateTaskVehicles(Long taskId, List<Long> newVehicleIds, String userName);
+     /*
      * @param taskId 浠诲姟ID
      * @param vehicleId 杞﹁締ID
      * @param remark 澶囨敞
+     * @param userId 鎿嶄綔浜篒D
+     * @param userName 鎿嶄綔浜虹敤鎴峰悕
      * @return 缁撴灉
      */
-    public int assignVehicleToTask(Long taskId, Long vehicleId, String remark);
+    int assignVehicleToTask(Long taskId, Long vehicleId, String remark, Long userId, String userName);
 
     /**
      * 鍙栨秷浠诲姟杞﹁締鍒嗛厤
@@ -119,7 +134,7 @@
      * @param vehicleId 杞﹁締ID
      * @return 缁撴灉
      */
-    public int unassignVehicleFromTask(Long taskId, Long vehicleId);
+    int unassignVehicleFromTask(Long taskId, Long vehicleId);
 
     /**
      * 鎵归噺鍒嗛厤杞﹁締缁欎换鍔�
@@ -127,9 +142,19 @@
      * @param taskId 浠诲姟ID
      * @param vehicleIds 杞﹁締ID鍒楄〃
      * @param remark 澶囨敞
+     * @param userId 鎿嶄綔浜篒D
+     * @param userName 鎿嶄綔浜虹敤鎴峰悕
      * @return 缁撴灉
      */
-    public int assignMultipleVehiclesToTask(Long taskId, List<Long> vehicleIds, String remark);
+    int assignMultipleVehiclesToTask(Long taskId, List<Long> vehicleIds, String remark, Long userId, String userName);
+
+    /**
+     * 鏌ヨ浠诲姟鍏宠仈鐨勮溅杈�
+     * 
+     * @param taskId 浠诲姟ID
+     * @return 浠诲姟杞﹁締鍏宠仈鍒楄〃
+     */
+    List<SysTaskVehicle> getTaskVehicles(Long taskId);
 
     /**
      * 鏌ヨ鍙敤杞﹁締
@@ -138,31 +163,22 @@
      * @param taskType 浠诲姟绫诲瀷
      * @return 鍙敤杞﹁締鍒楄〃
      */
-    public List<SysTaskVehicle> getAvailableVehicles(Long deptId, String taskType);
+    List<SysTaskVehicle> getAvailableVehicles(Long deptId, String taskType);
 
     /**
-     * 鏇存柊浠诲姟杞﹁締鍏宠仈鐘舵��
+     * 鍒犻櫎浠诲姟鐨勬墍鏈夎溅杈嗗叧鑱�
      * 
-     * @param id 鍏宠仈ID
-     * @param status 鏂扮姸鎬�
+     * @param taskId 浠诲姟ID
      * @return 缁撴灉
      */
-    public int updateTaskVehicleStatus(Long id, String status);
+    int deleteTaskVehiclesByTaskId(Long taskId);
 
     /**
-     * 鎵归噺鑾峰彇杞﹁締褰撳墠浠诲姟鐘舵��
+     * 妫�鏌ヨ溅杈嗘槸鍚﹀凡鍒嗛厤缁欎换鍔�
      * 
-     * @param vehicleIds 杞﹁締ID鍒楄〃
-     * @return Map<杞﹁締ID, Map<"taskCode": 浠诲姟缂栧彿, "taskStatus": 浠诲姟鐘舵��>>
+     * @param taskId 浠诲姟ID
+     * @param vehicleId 杞﹁締ID
+     * @return true-宸插垎閰嶏紝false-鏈垎閰�
      */
-    public Map<Long, Map<String, Object>> batchGetVehicleCurrentTaskStatus(List<Long> vehicleIds);
-
-    /**
-     * 鏇存柊浠诲姟杞﹁締鍏宠仈鐘舵�侊紙鏂版柟娉曪級
-     * 
-     * @param id 鍏宠仈ID
-     * @param status 鏂扮姸鎬�
-     * @return 缁撴灉
-     */
-    public int updateSysTaskVehicleStatus(Long id, String status);
+    boolean checkVehicleAssigned(Long taskId, Long vehicleId);
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysWelfareTaskService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysWelfareTaskService.java
new file mode 100644
index 0000000..285efba
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysWelfareTaskService.java
@@ -0,0 +1,46 @@
+package com.ruoyi.system.service;
+
+import com.ruoyi.system.domain.SysTaskWelfare;
+import com.ruoyi.system.domain.vo.TaskCreateVO;
+
+/**
+ * 绂忕杞︿换鍔℃湇鍔℃帴鍙�
+ * 
+ * @author ruoyi
+ */
+public interface ISysWelfareTaskService {
+
+    /**
+     * 淇濆瓨绂忕杞︿换鍔℃墿灞曚俊鎭�
+     * 
+     * @param taskId 浠诲姟ID
+     * @param userName 鎿嶄綔浜虹敤鎴峰悕
+     * @param createVO 浠诲姟鍒涘缓瀵硅薄
+     */
+    void saveWelfareInfo(Long taskId, String userName, TaskCreateVO createVO);
+
+    /**
+     * 鏇存柊绂忕杞︿换鍔℃墿灞曚俊鎭�
+     * 
+     * @param taskId 浠诲姟ID
+     * @param createVO 浠诲姟鍒涘缓/鏇存柊瀵硅薄
+     * @param userName 鎿嶄綔浜虹敤鎴峰悕
+     */
+    void updateWelfareInfo(Long taskId, TaskCreateVO createVO, String userName);
+
+    /**
+     * 鏍规嵁浠诲姟ID鏌ヨ绂忕杞︽墿灞曚俊鎭�
+     * 
+     * @param taskId 浠诲姟ID
+     * @return 绂忕杞︽墿灞曚俊鎭�
+     */
+    SysTaskWelfare getWelfareInfoByTaskId(Long taskId);
+
+    /**
+     * 鍒犻櫎绂忕杞︽墿灞曚俊鎭�
+     * 
+     * @param taskId 浠诲姟ID
+     * @return 缁撴灉
+     */
+    int deleteWelfareInfoByTaskId(Long taskId);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/CmsGpsCollectServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/CmsGpsCollectServiceImpl.java
index 2c6b036..b93684a 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/CmsGpsCollectServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/CmsGpsCollectServiceImpl.java
@@ -191,11 +191,11 @@
             // 瑙f瀽鍝嶅簲
             CmsVehicleLocationResponse locationResponse = JSONObject.parseObject(response, CmsVehicleLocationResponse.class);
             
-            if (locationResponse.getResult() == 0) {
-                log.info("鑾峰彇杞﹁締浣嶇疆淇℃伅鎴愬姛锛岃溅鐗屽彿锛歿}", vehicleId);
-            } else {
-                log.error("鑾峰彇杞﹁締浣嶇疆淇℃伅澶辫触锛岃溅鐗屽彿锛歿}", vehicleId);
-            }
+//            if (locationResponse.getResult() == 0) {
+//                log.info("鑾峰彇杞﹁締浣嶇疆淇℃伅鎴愬姛锛岃溅鐗屽彿锛歿}", vehicleId);
+//            } else {
+//                log.error("鑾峰彇杞﹁締浣嶇疆淇℃伅澶辫触锛岃溅鐗屽彿锛歿}", vehicleId);
+//            }
             
             return locationResponse;
         } catch (Exception e) {
@@ -258,11 +258,11 @@
             // 瑙f瀽鍝嶅簲
             CmsTrackDetailResponse trackResponse = JSONObject.parseObject(response, CmsTrackDetailResponse.class);
             
-            if (trackResponse.getResult() == 0) {
-                log.info("鑾峰彇璁惧鍘嗗彶杞ㄨ抗鎴愬姛锛岃澶囧彿锛歿}", devIdno);
-            } else {
-                log.error("鑾峰彇璁惧鍘嗗彶杞ㄨ抗澶辫触锛岃澶囧彿锛歿}", devIdno);
-            }
+//            if (trackResponse.getResult() == 0) {
+//                log.info("鑾峰彇璁惧鍘嗗彶杞ㄨ抗鎴愬姛锛岃澶囧彿锛歿}", devIdno);
+//            } else {
+//                log.error("鑾峰彇璁惧鍘嗗彶杞ㄨ抗澶辫触锛岃澶囧彿锛歿}", devIdno);
+//            }
             
             return trackResponse;
         } 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 4413acb..487b710 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
@@ -598,8 +598,20 @@
         // 鎿嶄綔鍛戒护
         params.put("DispatchOrd_Check", "0"); // 3=鐩存帴寮哄埗瀹屾垚
         
-        // 缁╂晥鍜岃垂鐢�
-        params.put("DispatchOrdPerfomance", emergency.getTransferPrice() != null ? emergency.getTransferPrice().toString() : "0");
+        // 缁╂晥鍜岃垂鐢細纭繚鏁板�煎瓧娈典笉涓簄ull
+        String transferPrice = "0";
+        if (emergency.getTransferPrice() != null) {
+            try {
+                transferPrice = emergency.getTransferPrice().toString();
+                if (transferPrice.contains(".")) {
+                    transferPrice = new java.math.BigDecimal(transferPrice).stripTrailingZeros().toPlainString();
+                }
+            } catch (Exception e) {
+                log.warn("杞崲杞繍浠锋牸澶辫触锛屼换鍔D: {}, 浣跨敤榛樿鍊�0", task.getTaskId(), e);
+                transferPrice = "0";
+            }
+        }
+        params.put("DispatchOrdPerfomance", transferPrice);
         params.put("StretcherMoney", "0"); // 鎶媴鏋惰垂
         params.put("AddMoneyType", ""); // 闄勫姞椤圭洰
         params.put("AddMoney", "0"); // 闄勫姞椤圭洰璐圭敤
@@ -632,7 +644,8 @@
         params.put("ServiceOrdPtDoctorPhone", ""); // 鎮h�呭尰鐢熺數璇�
         params.put("TransferModeID", ""); // 杞繍鏂瑰紡
         params.put("ServiceOrdVIP", "0"); // VIP瀹㈡埛
-        params.put("ServiceOrdTraTxnPrice", emergency.getTransferPrice() != null ? emergency.getTransferPrice().toString() : "0"); // 鎴愪氦浠�
+        // 浠锋牸瀛楁澶嶇敤锛岀‘淇濅竴鑷存��
+        params.put("ServiceOrdTraTxnPrice", transferPrice); // 鎴愪氦浠�
         params.put("ServiceOrdTraPrePayment", "0"); // 闇�棰勪粯娆�
         params.put("SettlementPrice", "0"); // 缁撶畻浠�
         params.put("ServiceOrdTraPriceReason", ""); // 宸环鍘熷洜
@@ -649,7 +662,21 @@
         params.put("ServiceOrdEstimatedOrderDate", ""); // 棰勮娲惧崟鏃堕棿
         params.put("ServiceOrdEstimatedOrderDateOld", ""); // 鍘熼璁℃淳鍗曟椂闂�
         params.put("ServiceOrdViaDistance", "0"); // 涓�旇窛绂�
-        params.put("ServiceOrdTraDistance", emergency.getTransferDistance() != null ? emergency.getTransferDistance().toString() : "0"); // 璺濈
+        // 璺濈瀛楁锛氱‘淇濅笉涓虹┖锛岄伩鍏嶆棫绯荤粺鎺ュ彛鎶ラ敊
+        String transferDistance = "0";
+        if (emergency.getTransferDistance() != null) {
+            try {
+                transferDistance = emergency.getTransferDistance().toString();
+                // 鍘婚櫎鍙兘鐨勫皬鏁扮偣鍚庡浣欑殑0
+                if (transferDistance.contains(".")) {
+                    transferDistance = new java.math.BigDecimal(transferDistance).stripTrailingZeros().toPlainString();
+                }
+            } catch (Exception e) {
+                log.warn("杞崲杞繍璺濈澶辫触锛屼换鍔D: {}, 浣跨敤榛樿鍊�0", task.getTaskId(), e);
+                transferDistance = "0";
+            }
+        }
+        params.put("ServiceOrdTraDistance", transferDistance);
         params.put("OrderLevel", "0"); // 鏌ョ湅绛夌骇
         params.put("ServiceOrdDepartureType", "1"); // 棰勭害绫诲瀷
         params.put("ConditionLevel", "0"); // 鐥呴噸绾у埆
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 7bdf7ab..4bf8466 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
@@ -6,6 +6,7 @@
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.system.domain.SysTaskEmergency;
 import com.ruoyi.system.domain.VehicleInfo;
+import com.ruoyi.system.domain.enums.TaskStatus;
 import com.ruoyi.system.domain.vo.TaskCreateVO;
 import com.ruoyi.system.domain.vo.TaskUpdateVO;
 import com.ruoyi.system.mapper.SysTaskEmergencyMapper;
@@ -17,6 +18,7 @@
 import com.ruoyi.system.mapper.VehicleInfoMapper;
 import com.ruoyi.system.service.ISysUserService;
 import com.ruoyi.system.service.IWechatTaskNotifyService;
+import com.ruoyi.system.utils.TaskStatusConverter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
@@ -198,6 +200,13 @@
         }
     }
 
+    /**
+     * 鏇存柊鍗曚釜杞繍鍗�
+     * @param serviceOrdID
+     * @param dispatchOrdID
+     * @param order
+     * @return
+     */
     private boolean updateTransferOrder(String serviceOrdID, String dispatchOrdID, Map<String, Object> order){
         log.info("寮�濮嬪悓姝ュ崟涓浆杩愬崟: ServiceOrdID={}, DispatchOrdID={}", serviceOrdID, dispatchOrdID);
         String sysTaskCode="";
@@ -209,13 +218,11 @@
             }
             // 鏋勯�燭askCreateVO瀵硅薄
             TaskCreateVO createTaskVo = buildCreateTaskVo(serviceOrdID, dispatchOrdID, order);
-
-            sysTaskCode = createTaskVo.getTaskCode();
             if (createTaskVo == null) {
                 log.error("鏋勯�燭askCreateVO澶辫触: ServiceOrdID={}, DispatchOrdID={}", serviceOrdID, dispatchOrdID);
                 return false;
             }
-
+            sysTaskCode = createTaskVo.getTaskCode();
             // 璁板綍鍒涘缓鐨勪换鍔′俊鎭�
             log.debug("鍑嗗鍒涘缓浠诲姟: ServiceOrdID={}, DispatchOrdID={}, 鎮h�呭鍚�={}, 杞嚭鍖婚櫌={}, 杞叆鍖婚櫌={}",
                     serviceOrdID, dispatchOrdID,
@@ -230,15 +237,26 @@
             String serviceOrdClass = getStringValue(order,"ServiceOrdClass");
             String serviceOrdNo = getStringValue(order,"ServiceOrdNo");
 
-            Integer oauserId=getIntegerValue(order,"ServiceOrd_NS_ID");
+            Integer oauserId=getIntegerValue(order,"ServiceOrd_CC_ID");
+            if(oauserId==null){
+                oauserId=getIntegerValue(order,"ServiceOrd_NS_ID");
+            }
+            if(oauserId==null || oauserId==0){
+                log.error("鍒涘缓浠诲姟鏃讹紝鑾峰彇鍒涘缓浜轰俊鎭け璐ワ紝serviceOrdID={}, DispatchOrdID={} ServiceOrd_NS_ID={},ServiceOrd_CC_ID={}", serviceOrdID, dispatchOrdID, getIntegerValue(order,"ServiceOrd_NS_ID"),getIntegerValue(order,"ServiceOrd_CC_ID"));
+                return false;
+            }
             SysUser sysUser=sysUserService.selectUserByOaUserId(oauserId);
             Long taskCreatorId=sysUser==null?null:sysUser.getUserId();
             String createUserName=sysUser==null?"system":sysUser.getUserName();
+            if(taskCreatorId==null || createUserName==null){
+                log.error("鍒涘缓浠诲姟鏃讹紝鑾峰彇鍒涘缓浜轰俊鎭け璐ワ紝serviceOrdID={}, DispatchOrdID={}", serviceOrdID, dispatchOrdID);
+                return false;
+            }
             SysDept dept=sysDeptService.selectDeptByServiceClass(serviceOrdClass);
             Long deptId=dept==null?null:dept.getDeptId();
             TaskUpdateVO updateTaskVo = new TaskUpdateVO();
             BeanUtils.copyProperties(createTaskVo, updateTaskVo);
-
+            log.info("寮�濮嬩繚瀛樿浆杩愪换鍔�,serviceOrdID={}, DispatchOrdID={}", serviceOrdID, dispatchOrdID);
 
             int result = sysTaskService.updateTask(updateTaskVo,serviceOrdID,dispatchOrdID, serviceOrdNo, taskCreatorId,createUserName, deptId, ServiceOrd_CC_Time, ServiceOrd_CC_Time);
 
@@ -300,11 +318,19 @@
              * 鍒涘缓浜篒D
              */
             Integer oauserId=getIntegerValue(order,"ServiceOrd_CC_ID");
+            if(oauserId==null || oauserId==0) {
+                oauserId=getIntegerValue(order,"ServiceOrd_NS_ID");
+            }
             SysUser sysUser=sysUserService.selectUserByOaUserId(oauserId);
-            Long taskCreatorId=sysUser==null?null:sysUser.getUserId();
-            String createUserName=sysUser==null?"system":sysUser.getUserName();
+            if(sysUser==null){
+                log.error("鍒涘缓浠诲姟鏃讹紝鑾峰彇鍒涘缓浜轰俊鎭け璐ワ紝serviceOrdID={}, DispatchOrdID={} ServiceOrd_CC_ID:{},ServiceOrd_NS_ID:{}", serviceOrdID, dispatchOrdID, getIntegerValue(order,"ServiceOrd_CC_ID"),getIntegerValue(order,"ServiceOrd_NS_ID"));
+                return false;
+            }
+            Long taskCreatorId= sysUser.getUserId();
+            String createUserName= sysUser.getUserName();
             SysDept dept=sysDeptService.selectDeptByServiceClass(serviceOrdClass);
             Long deptId=dept==null?null:dept.getDeptId();
+
 
             int result = sysTaskService.insertTask(createTaskVo,serviceOrdID,dispatchOrdID, serviceOrdNo, taskCreatorId,createUserName, deptId, ServiceOrd_CC_Time, ServiceOrd_CC_Time);
 
@@ -394,7 +420,7 @@
      */
     @Override
     public TaskCreateVO buildCreateTaskVo(String serviceOrdID, String dispatchOrdID, Map<String, Object> order) {
-        log.info("鏋勯�燭askCreateVO: ServiceOrdID={}, DispatchOrdID={}", serviceOrdID, dispatchOrdID);
+//        log.info("鏋勯�燭askCreateVO: ServiceOrdID={}, DispatchOrdID={}", serviceOrdID, dispatchOrdID);
 
         try {
             // 妫�鏌ュ弬鏁版湁鏁堟��
@@ -408,11 +434,11 @@
                 return null;
             }
             String serviceOrdClass = getStringValue(order, "ServiceOrdClass");
-            //TODO
+
             TaskCreateVO createTaskVo = new TaskCreateVO();
             String serviceOrdCode=this.getServiceOrdCode(getDateValue(order, "ServiceOrd_CC_Time"),serviceOrdClass,getStringValue(order, "ServiceOrdNo"));
             createTaskVo.setTaskCode(serviceOrdCode);
-            log.info("鏋勯�燭askCreateVO: ServiceOrdID={}, DispatchOrdID={},taskCode:{}", serviceOrdID, dispatchOrdID,serviceOrdCode);
+//            log.info("鏋勯�燭askCreateVO: ServiceOrdID={}, DispatchOrdID={},taskCode:{}", serviceOrdID, dispatchOrdID,serviceOrdCode);
             // 璁剧疆鍩烘湰淇℃伅
             createTaskVo.setTaskType("EMERGENCY_TRANSFER"); // 鎬ユ晳杞繍浠诲姟
             
@@ -544,7 +570,8 @@
                 String carLicense = legacyTransferSyncMapper.selectCarLicenseByCarID(carID);
                 if (StringUtils.isNotEmpty(carLicense)) {
                     // 鏍规嵁杞︾墝鍙锋煡璇㈡柊绯荤粺涓殑杞﹁締ID
-                    log.debug("杞﹁締杞︾墝鍙�: {}", carLicense);
+                    log.info("杞繍浠诲姟,ServiceOrdID:{},杞﹁締杞︾墝:{}",serviceOrdID,carLicense);
+//                    log.debug("杞﹁締杞︾墝鍙�: {}", carLicense);
                     
                     // 棣栧厛灏濊瘯閫氳繃VehicleInfoMapper鏌ヨ杞﹁締淇℃伅
                     try {
@@ -556,7 +583,7 @@
                             List<Long> vehicleIds = new ArrayList<>();
                             vehicleIds.add(vehicleInfo.getVehicleId());
                             createTaskVo.setVehicleIds(vehicleIds);
-                            log.debug("閫氳繃car_id鎵惧埌杞﹁締淇℃伅: vehicle_id={}, vehicle_no={}", vehicleInfo.getVehicleId(), vehicleInfo.getVehicleNo());
+//                            log.debug("閫氳繃car_id鎵惧埌杞﹁締淇℃伅: vehicle_id={}, vehicle_no={}", vehicleInfo.getVehicleId(), vehicleInfo.getVehicleNo());
                         } else {
                             // 濡傛灉閫氳繃car_id鎵句笉鍒帮紝灏濊瘯閫氳繃杞︾墝鍙锋煡璇�
                             vehicleInfo = vehicleInfoMapper.selectVehicleInfoByVehicleNo(carLicense);
@@ -564,9 +591,9 @@
                                 List<Long> vehicleIds = new ArrayList<>();
                                 vehicleIds.add(vehicleInfo.getVehicleId());
                                 createTaskVo.setVehicleIds(vehicleIds);
-                                log.debug("閫氳繃杞︾墝鍙锋壘鍒拌溅杈嗕俊鎭�: vehicle_id={}, vehicle_no={}", vehicleInfo.getVehicleId(), vehicleInfo.getVehicleNo());
+                                log.debug("杞繍浠诲姟,ServiceOrdID:{} 閫氳繃杞︾墝鍙锋壘鍒拌溅杈嗕俊鎭�: vehicle_id={}, vehicle_no={}",serviceOrdID, vehicleInfo.getVehicleId(), vehicleInfo.getVehicleNo());
                             } else {
-                                log.warn("鏈壘鍒板搴旂殑杞﹁締淇℃伅: car_id={}, vehicle_no={}", carID, carLicense);
+                                log.warn("杞繍浠诲姟,ServiceOrdID:{} 鏈壘鍒板搴旂殑杞﹁締淇℃伅: car_id={}, vehicle_no={}",serviceOrdID, carID, carLicense);
                             }
                         }
                     } catch (NumberFormatException e) {
@@ -585,6 +612,7 @@
                 }
             }
             
+
 
             // 璁剧疆鍏朵粬淇℃伅
             createTaskVo.setTaskDescription("浠庢棫绯荤粺鍚屾鐨勮浆杩愬崟");
@@ -635,8 +663,15 @@
                 }
                 createTaskVo.setDiseaseIds(diseaseIds);
             }
+
+            Integer dispatchOrdStatus = getIntegerValue(order, "DispatchOrdStatus");
+           TaskStatus status= TaskStatusConverter.convertFromLegacyStatus(dispatchOrdStatus);
+           if(status!=null) {
+               createTaskVo.setTaskStatus(status.getCode());
+           }
+
             
-            log.info("TaskCreateVO鏋勯�犲畬鎴�: ServiceOrdID={}, DispatchOrdID={}", serviceOrdID, dispatchOrdID);
+//            log.info("TaskCreateVO鏋勯�犲畬鎴�: ServiceOrdID={}, DispatchOrdID={}", serviceOrdID, dispatchOrdID);
             return createTaskVo;
             
         } catch (Exception e) {
@@ -652,6 +687,7 @@
      * @return 鎵ц浜轰俊鎭垪琛�
      */
     private List<TaskCreateVO.AssigneeInfo> queryAssignees(String dispatchOrdID) {
+        long startTime = System.currentTimeMillis();
         try {
             // 妫�鏌ュ弬鏁版湁鏁堟��
             if (StringUtils.isEmpty(dispatchOrdID)) {
@@ -659,8 +695,24 @@
                 return new ArrayList<>();
             }
             
-            // 浠嶴QL Server鏌ヨ鎵ц浜轰俊鎭�
-            List<Map<String, Object>> assigneeList = legacyTransferSyncMapper.selectAssigneesByDispatchOrdID(dispatchOrdID);
+            // 灏哠tring杞崲涓篖ong锛岄伩鍏嶆暟鎹簱绫诲瀷涓嶅尮閰嶅鑷寸殑鎬ц兘闂
+            Long dispatchOrdIdLong;
+            try {
+                dispatchOrdIdLong = Long.valueOf(dispatchOrdID);
+            } catch (NumberFormatException e) {
+                log.error("璋冨害鍗旾D鏍煎紡涓嶆纭�: {}", dispatchOrdID, e);
+                return new ArrayList<>();
+            }
+            
+            // 浠嶴QL Server鏌ヨ鎵ц浜轰俊鎭紙浣跨敤Long绫诲瀷锛屽尮閰岯IGINT瀛楁锛�
+            List<Map<String, Object>> assigneeList = legacyTransferSyncMapper.selectAssigneesByDispatchOrdID(dispatchOrdIdLong);
+            long queryTime = System.currentTimeMillis() - startTime;
+            
+            // 璁板綍鎱㈡煡璇紙瓒呰繃500ms锛�
+            if (queryTime > 500) {
+                log.warn("鏌ヨ鎵ц浜轰俊鎭�楁椂杩囬暱: {}ms, 璋冨害鍗旾D: {}, 寤鸿鍦� DispatchOrd_Entourage 琛ㄧ殑 DispatchOrdIDDt 瀛楁涓婂垱寤虹储寮�", 
+                    queryTime, dispatchOrdID);
+            }
             
             // 杞崲涓篢askCreateVO.AssigneeInfo瀵硅薄
             List<TaskCreateVO.AssigneeInfo> assignees = new ArrayList<>();
@@ -702,10 +754,11 @@
                 }
             }
             
-            log.info("鏌ヨ鍒皗}涓墽琛屼汉锛岃皟搴﹀崟ID: {}", assignees.size(), dispatchOrdID);
+            log.debug("鏌ヨ鍒皗}涓墽琛屼汉锛岃皟搴﹀崟ID: {}, 鑰楁椂: {}ms", assignees.size(), dispatchOrdID, System.currentTimeMillis() - startTime);
             return assignees;
         } catch (Exception e) {
-            log.error("鏌ヨ鎵ц浜轰俊鎭紓甯革紝璋冨害鍗旾D: {}", dispatchOrdID, e);
+            long totalTime = System.currentTimeMillis() - startTime;
+            log.error("鏌ヨ鎵ц浜轰俊鎭紓甯革紝璋冨害鍗旾D: {}, 鑰楁椂: {}ms", dispatchOrdID, totalTime, e);
             return new ArrayList<>(); // 杩斿洖绌哄垪琛ㄨ�屼笉鏄痭ull
         }
     }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotifyChannelConfigServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotifyChannelConfigServiceImpl.java
new file mode 100644
index 0000000..942d774
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotifyChannelConfigServiceImpl.java
@@ -0,0 +1,104 @@
+package com.ruoyi.system.service.impl;
+
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.system.domain.NotifyChannelConfig;
+import com.ruoyi.system.mapper.NotifyChannelConfigMapper;
+import com.ruoyi.system.service.INotifyChannelConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 閫氱煡娓犻亾閰嶇疆Service涓氬姟灞傚鐞�
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+@Service
+public class NotifyChannelConfigServiceImpl implements INotifyChannelConfigService {
+    @Autowired
+    private NotifyChannelConfigMapper notifyChannelConfigMapper;
+
+    /**
+     * 鏌ヨ閫氱煡娓犻亾閰嶇疆
+     * 
+     * @param id 涓婚敭ID
+     * @return 閫氱煡娓犻亾閰嶇疆
+     */
+    @Override
+    public NotifyChannelConfig selectNotifyChannelConfigById(Long id) {
+        return notifyChannelConfigMapper.selectNotifyChannelConfigById(id);
+    }
+
+    /**
+     * 鏌ヨ閫氱煡娓犻亾閰嶇疆鍒楄〃
+     * 
+     * @param notifyChannelConfig 鏌ヨ鏉′欢
+     * @return 閫氱煡娓犻亾閰嶇疆鍒楄〃
+     */
+    @Override
+    public List<NotifyChannelConfig> selectNotifyChannelConfigList(NotifyChannelConfig notifyChannelConfig) {
+        return notifyChannelConfigMapper.selectNotifyChannelConfigList(notifyChannelConfig);
+    }
+
+    /**
+     * 鏌ヨ鎸囧畾閫氱煡绫诲瀷鍚敤鐨勬笭閬撻厤缃�
+     * 鎸変紭鍏堢骇闄嶅簭鎺掑垪
+     * 
+     * @param notifyType 閫氱煡绫诲瀷
+     * @return 鍚敤鐨勬笭閬撻厤缃垪琛�
+     */
+    @Override
+    public List<NotifyChannelConfig> selectEnabledChannelsByType(String notifyType) {
+        return notifyChannelConfigMapper.selectEnabledChannelsByType(notifyType);
+    }
+
+    /**
+     * 妫�鏌ユ寚瀹氭笭閬撴槸鍚﹀惎鐢�
+     * 
+     * @param notifyType 閫氱煡绫诲瀷
+     * @param channel 娓犻亾
+     * @return 閰嶇疆淇℃伅
+     */
+    @Override
+    public NotifyChannelConfig selectByTypeAndChannel(String notifyType, String channel) {
+        return notifyChannelConfigMapper.selectByTypeAndChannel(notifyType, channel);
+    }
+
+    /**
+     * 鏂板閫氱煡娓犻亾閰嶇疆
+     * 
+     * @param notifyChannelConfig 閫氱煡娓犻亾閰嶇疆
+     * @return 缁撴灉
+     */
+    @Override
+    public int insertNotifyChannelConfig(NotifyChannelConfig notifyChannelConfig) {
+        notifyChannelConfig.setCreateTime(DateUtils.getNowDate());
+        notifyChannelConfig.setUpdateTime(DateUtils.getNowDate());
+        return notifyChannelConfigMapper.insertNotifyChannelConfig(notifyChannelConfig);
+    }
+
+    /**
+     * 淇敼閫氱煡娓犻亾閰嶇疆
+     * 
+     * @param notifyChannelConfig 閫氱煡娓犻亾閰嶇疆
+     * @return 缁撴灉
+     */
+    @Override
+    public int updateNotifyChannelConfig(NotifyChannelConfig notifyChannelConfig) {
+        notifyChannelConfig.setUpdateTime(DateUtils.getNowDate());
+        return notifyChannelConfigMapper.updateNotifyChannelConfig(notifyChannelConfig);
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎閫氱煡娓犻亾閰嶇疆
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑涓婚敭ID闆嗗悎
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteNotifyChannelConfigByIds(Long[] ids) {
+        return notifyChannelConfigMapper.deleteNotifyChannelConfigByIds(ids);
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotifyDispatchServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotifyDispatchServiceImpl.java
new file mode 100644
index 0000000..a918e86
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotifyDispatchServiceImpl.java
@@ -0,0 +1,321 @@
+package com.ruoyi.system.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.system.domain.*;
+import com.ruoyi.system.mapper.NotifyChannelConfigMapper;
+import com.ruoyi.system.mapper.SysMessageMapper;
+import com.ruoyi.system.service.*;
+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.Collections;
+import java.util.List;
+
+/**
+ * 閫氱煡鍒嗗彂鏈嶅姟瀹炵幇绫�
+ * 璐熻矗鏍规嵁閰嶇疆鍐冲畾鍙戦�佹笭閬撳苟鎵ц鍒嗗彂
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+@Service
+public class NotifyDispatchServiceImpl implements INotifyDispatchService {
+
+    private static final Logger log = LoggerFactory.getLogger(NotifyDispatchServiceImpl.class);
+
+    @Autowired
+    private NotifyChannelConfigMapper channelConfigMapper;
+
+    @Autowired
+    private INotifyTaskService notifyTaskService;
+
+    @Autowired
+    private INotifySendLogService notifySendLogService;
+
+    @Autowired
+    private SysMessageMapper sysMessageMapper;
+
+    @Autowired
+    private IWechatTaskNotifyService wechatTaskNotifyService;
+
+    @Autowired
+    private ISmsService smsService;
+
+    /**
+     * 鑾峰彇鎸囧畾閫氱煡绫诲瀷鍚敤鐨勬笭閬撳垪琛�
+     */
+    @Override
+    public List<NotifyChannelConfig> getEnabledChannels(String notifyType) {
+        if (StringUtils.isEmpty(notifyType)) {
+            return Collections.emptyList();
+        }
+        return channelConfigMapper.selectEnabledChannelsByType(notifyType);
+    }
+
+    /**
+     * 妫�鏌ユ寚瀹氭笭閬撴槸鍚﹀惎鐢�
+     */
+    @Override
+    public boolean isChannelEnabled(String notifyType, String channel) {
+        NotifyChannelConfig config = channelConfigMapper.selectByTypeAndChannel(notifyType, channel);
+        return config != null && NotifyChannelConfig.ENABLED_YES.equals(config.getEnabled());
+    }
+
+    /**
+     * 鍒嗗彂閫氱煡浠诲姟
+     */
+    @Override
+    public int dispatchNotify(NotifyTask notifyTask) {
+        if (notifyTask == null) {
+            return 0;
+        }
+
+        log.info("寮�濮嬪垎鍙戦�氱煡浠诲姟锛宨d={}, taskId={}, userId={}, notifyType={}", 
+                notifyTask.getId(), notifyTask.getTaskId(), notifyTask.getUserId(), notifyTask.getNotifyType());
+
+        // 鏇存柊鐘舵�佷负澶勭悊涓�
+        notifyTaskService.markProcessing(notifyTask.getId());
+
+        // 鑾峰彇鍚敤鐨勬笭閬�
+        List<NotifyChannelConfig> channels = getEnabledChannels(notifyTask.getNotifyType());
+        if (channels.isEmpty()) {
+            log.warn("娌℃湁鍚敤鐨勬笭閬擄紝notifyType={}", notifyTask.getNotifyType());
+            notifyTaskService.markCompleted(notifyTask.getId());
+            return 0;
+        }
+
+        int successCount = 0;
+        StringBuilder errorMsgs = new StringBuilder();
+
+        // 鎸変紭鍏堢骇渚濇鍙戦�佸悇娓犻亾
+        for (NotifyChannelConfig channelConfig : channels) {
+            String channel = channelConfig.getChannel();
+            boolean success = false;
+            String errorMsg = null;
+
+            try {
+                switch (channel) {
+                    case NotifyChannelConfig.CHANNEL_SITE_MSG:
+                        success = sendSiteMessage(notifyTask);
+                        break;
+                    case NotifyChannelConfig.CHANNEL_WECHAT:
+                        success = sendWechatMessage(notifyTask);
+                        break;
+                    case NotifyChannelConfig.CHANNEL_SMS:
+                        success = sendSmsMessage(notifyTask);
+                        break;
+                    default:
+                        log.warn("涓嶆敮鎸佺殑娓犻亾绫诲瀷锛歿}", channel);
+                        continue;
+                }
+            } catch (Exception e) {
+                errorMsg = e.getMessage();
+                log.error("鍙戦�亄}娑堟伅澶辫触锛宯otifyTaskId={}", channel, notifyTask.getId(), e);
+            }
+
+            // 璁板綍鍙戦�佹棩蹇�
+            recordSendLog(notifyTask, channel, success, errorMsg);
+
+            if (success) {
+                successCount++;
+                log.info("{}娓犻亾鍙戦�佹垚鍔燂紝notifyTaskId={}", channel, notifyTask.getId());
+            } else {
+                if (errorMsgs.length() > 0) {
+                    errorMsgs.append("; ");
+                }
+                errorMsgs.append(channel).append(":").append(errorMsg != null ? errorMsg : "鍙戦�佸け璐�");
+            }
+        }
+
+        // 鏇存柊浠诲姟鐘舵��
+        if (successCount > 0) {
+            notifyTaskService.markCompleted(notifyTask.getId());
+        } else {
+            notifyTaskService.incrementRetryCount(notifyTask.getId());
+            // 妫�鏌ユ槸鍚﹁秴杩囨渶澶ч噸璇曟鏁�
+            NotifyTask updated = notifyTaskService.selectNotifyTaskById(notifyTask.getId());
+            if (updated.getRetryCount() >= updated.getMaxRetry()) {
+                notifyTaskService.markFailed(notifyTask.getId(), errorMsgs.toString());
+            } else {
+                // 閲嶇疆涓哄緟澶勭悊鐘舵�侊紝绛夊緟涓嬫閲嶈瘯
+                notifyTaskService.updateNotifyTask(createStatusUpdate(notifyTask.getId(), NotifyTask.STATUS_PENDING));
+            }
+        }
+
+        log.info("閫氱煡浠诲姟鍒嗗彂瀹屾垚锛宨d={}, 鎴愬姛娓犻亾鏁�={}", notifyTask.getId(), successCount);
+        return successCount;
+    }
+
+    /**
+     * 鍒涘缓鐘舵�佹洿鏂板璞�
+     */
+    private NotifyTask createStatusUpdate(Long id, String status) {
+        NotifyTask update = new NotifyTask();
+        update.setId(id);
+        update.setStatus(status);
+        return update;
+    }
+
+    /**
+     * 璁板綍鍙戦�佹棩蹇�
+     */
+    private void recordSendLog(NotifyTask notifyTask, String channel, boolean success, String errorMsg) {
+        try {
+            NotifySendLog sendLog = new NotifySendLog();
+            sendLog.setNotifyTaskId(notifyTask.getId());
+            sendLog.setTaskId(notifyTask.getTaskId());
+            sendLog.setUserId(notifyTask.getUserId());
+            sendLog.setUserName(notifyTask.getUserName());
+            sendLog.setNotifyType(notifyTask.getNotifyType());
+            sendLog.setChannel(channel);
+            sendLog.setSendStatus(success ? NotifySendLog.SEND_STATUS_SUCCESS : NotifySendLog.SEND_STATUS_FAILED);
+            sendLog.setSendTime(DateUtils.getNowDate());
+            sendLog.setResponseMsg(errorMsg);
+            
+            notifySendLogService.insertNotifySendLog(sendLog);
+        } catch (Exception e) {
+            log.error("璁板綍鍙戦�佹棩蹇楀け璐�", e);
+        }
+    }
+
+    /**
+     * 鎵归噺鍒嗗彂閫氱煡浠诲姟
+     */
+    @Override
+    public int dispatchNotifies(List<NotifyTask> notifyTasks) {
+        if (notifyTasks == null || notifyTasks.isEmpty()) {
+            return 0;
+        }
+
+        int successCount = 0;
+        for (NotifyTask notifyTask : notifyTasks) {
+            int channelCount = dispatchNotify(notifyTask);
+            if (channelCount > 0) {
+                successCount++;
+            }
+        }
+        return successCount;
+    }
+
+    /**
+     * 澶勭悊寰呭彂閫佺殑閫氱煡浠诲姟
+     */
+    @Override
+    public int processPendingNotifies(int limit) {
+        List<NotifyTask> pendingTasks = notifyTaskService.selectPendingNotifyTasks(limit);
+        if (pendingTasks.isEmpty()) {
+            return 0;
+        }
+
+        log.info("寮�濮嬪鐞嗗緟鍙戦�侀�氱煡浠诲姟锛屾暟閲�={}", pendingTasks.size());
+        return dispatchNotifies(pendingTasks);
+    }
+
+    /**
+     * 鍙戦�佺珯鍐呮秷鎭�
+     */
+    @Override
+    public boolean sendSiteMessage(NotifyTask notifyTask) {
+        try {
+            SysMessage message = new SysMessage();
+            message.setMessageType(getMessageType(notifyTask.getNotifyType()));
+            message.setMessageTitle(notifyTask.getTitle());
+            message.setMessageContent(notifyTask.getContent());
+            message.setTaskId(notifyTask.getTaskId());
+            message.setTaskCode(notifyTask.getTaskCode());
+            message.setReceiverId(notifyTask.getUserId());
+            message.setReceiverName(notifyTask.getUserName());
+            message.setSenderId(0L);
+            message.setSenderName("绯荤粺");
+            message.setIsRead("0");
+            message.setCreateTime(DateUtils.getNowDate());
+            message.setDelFlag("0");
+
+            sysMessageMapper.insertSysMessage(message);
+            log.info("绔欏唴娑堟伅鍙戦�佹垚鍔燂紝messageId={}, userId={}", message.getMessageId(), notifyTask.getUserId());
+            return true;
+        } catch (Exception e) {
+            log.error("绔欏唴娑堟伅鍙戦�佸け璐ワ紝userId={}", notifyTask.getUserId(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 鏍规嵁閫氱煡绫诲瀷鑾峰彇娑堟伅绫诲瀷
+     */
+    private String getMessageType(String notifyType) {
+        switch (notifyType) {
+            case NotifyTask.NOTIFY_TYPE_TASK_ASSIGN:
+                return "PUSH";
+            case NotifyTask.NOTIFY_TYPE_STATUS_CHANGE:
+                return "STATUS";
+            case NotifyTask.NOTIFY_TYPE_TASK_CREATE:
+                return "CREATE";
+            default:
+                return "SYSTEM";
+        }
+    }
+
+    /**
+     * 鍙戦�佸井淇¤闃呮秷鎭�
+     */
+    @Override
+    public boolean sendWechatMessage(NotifyTask notifyTask) {
+        try {
+            // 璋冪敤寰俊閫氱煡鏈嶅姟
+            int result = wechatTaskNotifyService.sendTaskNotifyMessage(
+                    notifyTask.getTaskId(), 
+                    Collections.singletonList(notifyTask.getUserId()), 
+                    null  // 涓嶆帓闄や换浣曠敤鎴�
+            );
+            
+            if (result > 0) {
+                log.info("寰俊璁㈤槄娑堟伅鍙戦�佹垚鍔燂紝taskId={}, userId={}", notifyTask.getTaskId(), notifyTask.getUserId());
+                return true;
+            } else {
+                log.warn("寰俊璁㈤槄娑堟伅鍙戦�佸け璐ワ紝taskId={}, userId={}", notifyTask.getTaskId(), notifyTask.getUserId());
+                return false;
+            }
+        } catch (Exception e) {
+            log.error("寰俊璁㈤槄娑堟伅鍙戦�佸紓甯革紝taskId={}, userId={}", notifyTask.getTaskId(), notifyTask.getUserId(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 鍙戦�佺煭淇℃秷鎭�
+     */
+    @Override
+    public boolean sendSmsMessage(NotifyTask notifyTask) {
+        try {
+            // 妫�鏌ョ煭淇℃湇鍔℃槸鍚﹀惎鐢�
+            if (!smsService.isEnabled()) {
+                log.info("鐭俊鏈嶅姟宸插叧闂紝璺宠繃鍙戦��");
+                return false;
+            }
+
+            String phone = notifyTask.getUserPhone();
+            if (StringUtils.isEmpty(phone)) {
+                log.warn("鐢ㄦ埛鎵嬫満鍙蜂负绌猴紝鏃犳硶鍙戦�佺煭淇★紝userId={}", notifyTask.getUserId());
+                return false;
+            }
+
+            // 鍙戦�佺煭淇�
+            boolean success = smsService.sendSms(phone, notifyTask.getContent());
+            if (success) {
+                log.info("鐭俊鍙戦�佹垚鍔燂紝phone={}, userId={}", phone, notifyTask.getUserId());
+            } else {
+                log.warn("鐭俊鍙戦�佸け璐ワ紝phone={}, userId={}", phone, notifyTask.getUserId());
+            }
+            return success;
+        } catch (Exception e) {
+            log.error("鐭俊鍙戦�佸紓甯革紝userId={}", notifyTask.getUserId(), e);
+            return false;
+        }
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotifySendLogServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotifySendLogServiceImpl.java
new file mode 100644
index 0000000..f671855
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotifySendLogServiceImpl.java
@@ -0,0 +1,214 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.Date;
+import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.system.domain.NotifySendLog;
+import com.ruoyi.system.mapper.NotifySendLogMapper;
+import com.ruoyi.system.service.INotifySendLogService;
+
+/**
+ * 閫氱煡鍙戦�佽褰曟湇鍔″疄鐜扮被
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+@Service
+public class NotifySendLogServiceImpl implements INotifySendLogService {
+
+    private static final Logger log = LoggerFactory.getLogger(NotifySendLogServiceImpl.class);
+
+    @Autowired
+    private NotifySendLogMapper notifySendLogMapper;
+
+    /**
+     * 鏌ヨ閫氱煡鍙戦�佽褰�
+     * 
+     * @param id 閫氱煡鍙戦�佽褰曚富閿�
+     * @return 閫氱煡鍙戦�佽褰�
+     */
+    @Override
+    public NotifySendLog selectNotifySendLogById(Long id) {
+        return notifySendLogMapper.selectNotifySendLogById(id);
+    }
+
+    /**
+     * 鏌ヨ閫氱煡鍙戦�佽褰曞垪琛�
+     * 
+     * @param notifySendLog 閫氱煡鍙戦�佽褰�
+     * @return 閫氱煡鍙戦�佽褰�
+     */
+    @Override
+    public List<NotifySendLog> selectNotifySendLogList(NotifySendLog notifySendLog) {
+        return notifySendLogMapper.selectNotifySendLogList(notifySendLog);
+    }
+
+    /**
+     * 妫�鏌ユ槸鍚﹀凡鍙戦�佽繃閫氱煡锛堥槻閲嶆鏌ワ級
+     * 
+     * @param taskId 浠诲姟ID
+     * @param userId 鐢ㄦ埛ID
+     * @param notifyType 閫氱煡绫诲瀷
+     * @param channel 閫氱煡娓犻亾
+     * @return true=宸插彂閫佽繃, false=鏈彂閫佽繃
+     */
+    @Override
+    public boolean hasNotified(Long taskId, Long userId, String notifyType, String channel) {
+        int count = notifySendLogMapper.checkNotifySendLogExists(taskId, userId, notifyType, channel);
+        return count > 0;
+    }
+
+    /**
+     * 灏濊瘯鍒涘缓鍙戦�佽褰曪紙闃查噸锛屽鏋滃凡瀛樺湪鍒欒繑鍥瀗ull锛�
+     * 浣跨敤鏁版嵁搴撳敮涓�绱㈠紩淇濊瘉闃查噸
+     * 
+     * @param taskId 浠诲姟ID
+     * @param userId 鐢ㄦ埛ID
+     * @param userName 鐢ㄦ埛濮撳悕
+     * @param notifyType 閫氱煡绫诲瀷
+     * @param channel 閫氱煡娓犻亾
+     * @return 鍒涘缓鐨勮褰曪紝濡傛灉宸插瓨鍦ㄥ垯杩斿洖null
+     */
+    @Override
+    @Transactional
+    public NotifySendLog tryCreateSendLog(Long taskId, Long userId, String userName,
+                                           String notifyType, String channel) {
+        // 鍏堟鏌ユ槸鍚﹀凡瀛樺湪
+        if (hasNotified(taskId, userId, notifyType, channel)) {
+            log.debug("閫氱煡璁板綍宸插瓨鍦紝璺宠繃鍒涘缓锛宼askId={}, userId={}, notifyType={}, channel={}",
+                    taskId, userId, notifyType, channel);
+            return null;
+        }
+
+        try {
+            // 鍒涘缓鏂拌褰�
+            NotifySendLog sendLog = new NotifySendLog();
+            sendLog.setTaskId(taskId);
+            sendLog.setUserId(userId);
+            sendLog.setUserName(userName);
+            sendLog.setNotifyType(notifyType);
+            sendLog.setChannel(channel);
+            sendLog.setSendStatus(NotifySendLog.SEND_STATUS_PENDING);
+            sendLog.setRetryCount(0);
+            sendLog.setCreateTime(DateUtils.getNowDate());
+            sendLog.setCreateBy("system");
+
+            int result = notifySendLogMapper.insertNotifySendLog(sendLog);
+            if (result > 0) {
+                log.info("鍒涘缓閫氱煡鍙戦�佽褰曟垚鍔燂紝id={}, taskId={}, userId={}, notifyType={}, channel={}",
+                        sendLog.getId(), taskId, userId, notifyType, channel);
+                return sendLog;
+            }
+        } catch (Exception e) {
+            // 鍙兘鏄苟鍙戞彃鍏ュ鑷寸殑鍞竴绱㈠紩鍐茬獊锛岃涓哄凡瀛樺湪
+            log.warn("鍒涘缓閫氱煡鍙戦�佽褰曞け璐ワ紙鍙兘鏄苟鍙戦噸澶嶏級锛宼askId={}, userId={}, notifyType={}, channel={}, error={}",
+                    taskId, userId, notifyType, channel, e.getMessage());
+        }
+
+        return null;
+    }
+
+    /**
+     * 鏇存柊鍙戦�佺姸鎬佷负鎴愬姛
+     * 
+     * @param id 璁板綍ID
+     * @param result 鍙戦�佺粨鏋滀俊鎭�
+     */
+    @Override
+    public void markSendSuccess(Long id, String result) {
+        if (id == null) {
+            return;
+        }
+        try {
+            notifySendLogMapper.updateSendStatus(id, NotifySendLog.SEND_STATUS_SUCCESS, result);
+            log.debug("鏇存柊閫氱煡鍙戦�佺姸鎬佷负鎴愬姛锛宨d={}", id);
+        } catch (Exception e) {
+            log.error("鏇存柊閫氱煡鍙戦�佺姸鎬佸け璐ワ紝id={}", id, e);
+        }
+    }
+
+    /**
+     * 鏇存柊鍙戦�佺姸鎬佷负澶辫触
+     * 
+     * @param id 璁板綍ID
+     * @param errorMsg 閿欒淇℃伅
+     */
+    @Override
+    public void markSendFailed(Long id, String errorMsg) {
+        if (id == null) {
+            return;
+        }
+        try {
+            // 闄愬埗閿欒淇℃伅闀垮害
+            if (errorMsg != null && errorMsg.length() > 500) {
+                errorMsg = errorMsg.substring(0, 500);
+            }
+            notifySendLogMapper.updateSendStatus(id, NotifySendLog.SEND_STATUS_FAILED, errorMsg);
+            log.debug("鏇存柊閫氱煡鍙戦�佺姸鎬佷负澶辫触锛宨d={}, error={}", id, errorMsg);
+        } catch (Exception e) {
+            log.error("鏇存柊閫氱煡鍙戦�佺姸鎬佸け璐ワ紝id={}", id, e);
+        }
+    }
+
+    /**
+     * 鏂板閫氱煡鍙戦�佽褰�
+     * 
+     * @param notifySendLog 閫氱煡鍙戦�佽褰�
+     * @return 缁撴灉
+     */
+    @Override
+    public int insertNotifySendLog(NotifySendLog notifySendLog) {
+        notifySendLog.setCreateTime(DateUtils.getNowDate());
+        return notifySendLogMapper.insertNotifySendLog(notifySendLog);
+    }
+
+    /**
+     * 淇敼閫氱煡鍙戦�佽褰�
+     * 
+     * @param notifySendLog 閫氱煡鍙戦�佽褰�
+     * @return 缁撴灉
+     */
+    @Override
+    public int updateNotifySendLog(NotifySendLog notifySendLog) {
+        notifySendLog.setUpdateTime(DateUtils.getNowDate());
+        return notifySendLogMapper.updateNotifySendLog(notifySendLog);
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎閫氱煡鍙戦�佽褰�
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑閫氱煡鍙戦�佽褰曚富閿�
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteNotifySendLogByIds(Long[] ids) {
+        return notifySendLogMapper.deleteNotifySendLogByIds(ids);
+    }
+
+    /**
+     * 鍒犻櫎閫氱煡鍙戦�佽褰曚俊鎭�
+     * 
+     * @param id 閫氱煡鍙戦�佽褰曚富閿�
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteNotifySendLogById(Long id) {
+        return notifySendLogMapper.deleteNotifySendLogById(id);
+    }
+
+    /**
+     * 鏌ヨ寰呴噸璇曠殑澶辫触璁板綍
+     * 
+     * @param maxRetryCount 鏈�澶ч噸璇曟鏁�
+     * @return 澶辫触璁板綍鍒楄〃
+     */
+    @Override
+    public List<NotifySendLog> selectFailedNotifySendLogs(Integer maxRetryCount) {
+        return notifySendLogMapper.selectFailedNotifySendLogs(maxRetryCount);
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotifyTaskServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotifyTaskServiceImpl.java
new file mode 100644
index 0000000..98e0ad3
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/NotifyTaskServiceImpl.java
@@ -0,0 +1,166 @@
+package com.ruoyi.system.service.impl;
+
+import com.ruoyi.system.domain.NotifyTask;
+import com.ruoyi.system.mapper.NotifyTaskMapper;
+import com.ruoyi.system.service.INotifyTaskService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 閫氱煡浠诲姟鏈嶅姟瀹炵幇绫�
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+@Service
+public class NotifyTaskServiceImpl implements INotifyTaskService {
+
+    private static final Logger log = LoggerFactory.getLogger(NotifyTaskServiceImpl.class);
+
+    @Autowired
+    private NotifyTaskMapper notifyTaskMapper;
+
+    /**
+     * 鏌ヨ閫氱煡浠诲姟
+     */
+    @Override
+    public NotifyTask selectNotifyTaskById(Long id) {
+        return notifyTaskMapper.selectNotifyTaskById(id);
+    }
+
+    /**
+     * 鏌ヨ閫氱煡浠诲姟鍒楄〃
+     */
+    @Override
+    public List<NotifyTask> selectNotifyTaskList(NotifyTask notifyTask) {
+        return notifyTaskMapper.selectNotifyTaskList(notifyTask);
+    }
+
+    /**
+     * 鏌ヨ寰呭鐞嗙殑閫氱煡浠诲姟
+     */
+    @Override
+    public List<NotifyTask> selectPendingNotifyTasks(int limit) {
+        return notifyTaskMapper.selectPendingNotifyTasks(limit);
+    }
+
+    /**
+     * 妫�鏌ラ�氱煡浠诲姟鏄惁宸插瓨鍦�
+     */
+    @Override
+    public boolean existsNotifyTask(Long taskId, Long userId, String notifyType) {
+        int count = notifyTaskMapper.countByTaskUserType(taskId, userId, notifyType);
+        return count > 0;
+    }
+
+    /**
+     * 鍒涘缓閫氱煡浠诲姟锛堝甫闃查噸锛�
+     */
+    @Override
+    public NotifyTask createNotifyTask(NotifyTask notifyTask) {
+        // 妫�鏌ユ槸鍚﹀凡瀛樺湪
+        if (existsNotifyTask(notifyTask.getTaskId(), notifyTask.getUserId(), notifyTask.getNotifyType())) {
+            log.info("閫氱煡浠诲姟宸插瓨鍦紝璺宠繃鍒涘缓锛宼askId={}, userId={}, notifyType={}", 
+                    notifyTask.getTaskId(), notifyTask.getUserId(), notifyTask.getNotifyType());
+            return null;
+        }
+
+        // 璁剧疆榛樿鍊�
+        if (notifyTask.getStatus() == null) {
+            notifyTask.setStatus(NotifyTask.STATUS_PENDING);
+        }
+        if (notifyTask.getRetryCount() == null) {
+            notifyTask.setRetryCount(0);
+        }
+        if (notifyTask.getMaxRetry() == null) {
+            notifyTask.setMaxRetry(3);
+        }
+
+        try {
+            notifyTaskMapper.insertNotifyTask(notifyTask);
+            log.info("鍒涘缓閫氱煡浠诲姟鎴愬姛锛宨d={}, taskId={}, userId={}, notifyType={}", 
+                    notifyTask.getId(), notifyTask.getTaskId(), notifyTask.getUserId(), notifyTask.getNotifyType());
+            return notifyTask;
+        } catch (Exception e) {
+            // 鍙兘鏄敮涓�绱㈠紩鍐茬獊锛堝苟鍙戞儏鍐碉級
+            log.warn("鍒涘缓閫氱煡浠诲姟澶辫触锛堝彲鑳藉苟鍙戝啿绐侊級锛宼askId={}, userId={}, notifyType={}", 
+                    notifyTask.getTaskId(), notifyTask.getUserId(), notifyTask.getNotifyType(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 鎵归噺鍒涘缓閫氱煡浠诲姟
+     */
+    @Override
+    public int createNotifyTasks(List<NotifyTask> notifyTasks) {
+        int successCount = 0;
+        for (NotifyTask notifyTask : notifyTasks) {
+            NotifyTask created = createNotifyTask(notifyTask);
+            if (created != null) {
+                successCount++;
+            }
+        }
+        return successCount;
+    }
+
+    /**
+     * 淇敼閫氱煡浠诲姟
+     */
+    @Override
+    public int updateNotifyTask(NotifyTask notifyTask) {
+        return notifyTaskMapper.updateNotifyTask(notifyTask);
+    }
+
+    /**
+     * 鏇存柊閫氱煡浠诲姟鐘舵�佷负澶勭悊涓�
+     */
+    @Override
+    public int markProcessing(Long id) {
+        return notifyTaskMapper.updateNotifyTaskStatus(id, NotifyTask.STATUS_PROCESSING, null);
+    }
+
+    /**
+     * 鏇存柊閫氱煡浠诲姟鐘舵�佷负瀹屾垚
+     */
+    @Override
+    public int markCompleted(Long id) {
+        return notifyTaskMapper.updateNotifyTaskStatus(id, NotifyTask.STATUS_COMPLETED, null);
+    }
+
+    /**
+     * 鏇存柊閫氱煡浠诲姟鐘舵�佷负澶辫触
+     */
+    @Override
+    public int markFailed(Long id, String errorMsg) {
+        return notifyTaskMapper.updateNotifyTaskStatus(id, NotifyTask.STATUS_FAILED, errorMsg);
+    }
+
+    /**
+     * 澧炲姞閲嶈瘯娆℃暟
+     */
+    @Override
+    public int incrementRetryCount(Long id) {
+        return notifyTaskMapper.incrementRetryCount(id);
+    }
+
+    /**
+     * 鍒犻櫎閫氱煡浠诲姟
+     */
+    @Override
+    public int deleteNotifyTaskById(Long id) {
+        return notifyTaskMapper.deleteNotifyTaskById(id);
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎閫氱煡浠诲姟
+     */
+    @Override
+    public int deleteNotifyTaskByIds(Long[] ids) {
+        return notifyTaskMapper.deleteNotifyTaskByIds(ids);
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentSyncServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentSyncServiceImpl.java
index ca8b487..67b86f2 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentSyncServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentSyncServiceImpl.java
@@ -115,14 +115,16 @@
             PaidMoney paidMoney = new PaidMoney();
             paidMoney.setPaidMoneyClass("FI"); // 榛樿FI
             paidMoney.setServiceOrdIDDt(emergency.getLegacyServiceOrdId());
-            paidMoney.setDispatchOrdIDDt(emergency.getLegacyDispatchOrdId());
+            if(emergency.getLegacyDispatchOrdId()!=null) {
+                paidMoney.setDispatchOrdIDDt(emergency.getLegacyDispatchOrdId());
+            }
             paidMoney.setPaidMoney(payment.getSettlementAmount());
             paidMoney.setPaidMoneyType(convertPaymentMethodToLegacy(payment.getPaymentMethod()));
             paidMoney.setPaidMoneyMono(payment.getTradeNo() != null ? payment.getTradeNo() : payment.getOutTradeNo());
             paidMoney.setPaidMoneyTime(payment.getPayTime() != null ? payment.getPayTime() : new Date());
             paidMoney.setPaidMoneyOaID(oaUserId);
             paidMoney.setPaidMoneyUnitID(0); // 榛樿涓�0
-            paidMoney.setPaidMoneyAPCheck(1); // 宸茬‘璁�
+            paidMoney.setPaidMoneyAPCheck(0); // 宸茬‘璁�
             paidMoney.setPaidMoneyAPTime(new Date());
             paidMoney.setPaidMoneyTimestamp(String.valueOf(System.currentTimeMillis()));
 
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SmsServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SmsServiceImpl.java
new file mode 100644
index 0000000..61a8ef4
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SmsServiceImpl.java
@@ -0,0 +1,298 @@
+package com.ruoyi.system.service.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.common.config.SmsConfig;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.utils.SmsUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.system.domain.SysTask;
+import com.ruoyi.system.domain.SysTaskEmergency;
+import com.ruoyi.system.mapper.SysTaskEmergencyMapper;
+import com.ruoyi.system.mapper.SysTaskMapper;
+import com.ruoyi.system.mapper.SysUserMapper;
+import com.ruoyi.system.service.ISmsService;
+import com.ruoyi.system.service.ISysConfigService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 鐭俊鍙戦�佹湇鍔″疄鐜扮被
+ * 
+ * @author ruoyi
+ * @date 2025-12-07
+ */
+@Service
+public class SmsServiceImpl implements ISmsService {
+
+    private static final Logger log = LoggerFactory.getLogger(SmsServiceImpl.class);
+
+    /** 绯荤粺閰嶇疆閿細鐭俊寮�鍏� */
+    private static final String CONFIG_KEY_SMS_ENABLED = "sms.enabled";
+
+    @Autowired
+    private SmsConfig smsConfig;
+
+    @Autowired
+    private ISysConfigService configService;
+
+    @Autowired
+    private SysUserMapper sysUserMapper;
+
+    @Autowired
+    private SysTaskMapper sysTaskMapper;
+
+    @Autowired
+    private SysTaskEmergencyMapper sysTaskEmergencyMapper;
+
+    /**
+     * 妫�鏌ョ煭淇℃湇鍔℃槸鍚﹀惎鐢�
+     * 浼樺厛浠庢暟鎹簱閰嶇疆璇诲彇锛屽叾娆′粠閰嶇疆鏂囦欢璇诲彇
+     * 
+     * @return true=鍚敤, false=绂佺敤
+     */
+    @Override
+    public boolean isEnabled() {
+        try {
+            // 浼樺厛浠庢暟鎹簱閰嶇疆璇诲彇
+            String enabled = configService.selectConfigByKey(CONFIG_KEY_SMS_ENABLED);
+            if (StringUtils.isNotEmpty(enabled)) {
+                return "true".equalsIgnoreCase(enabled);
+            }
+        } catch (Exception e) {
+            log.warn("鑾峰彇鐭俊寮�鍏抽厤缃け璐ワ紝浣跨敤閰嶇疆鏂囦欢閰嶇疆", e);
+        }
+        // 浠庨厤缃枃浠惰鍙�
+        return smsConfig.isEnabled();
+    }
+
+    /**
+     * 鍙戦�佺煭淇�
+     * 
+     * @param phone 鎵嬫満鍙�
+     * @param content 鐭俊鍐呭
+     * @return 鏄惁鍙戦�佹垚鍔�
+     */
+    @Override
+    public boolean sendSms(String phone, String content) {
+        if (!isEnabled()) {
+            log.info("鐭俊鏈嶅姟宸插叧闂紝璺宠繃鍙戦�侊紝phone={}", phone);
+            return false;
+        }
+
+        if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(content)) {
+            log.warn("鐭俊鍙戦�佸弬鏁颁笉瀹屾暣锛宲hone={}, content={}", phone, content);
+            return false;
+        }
+
+        try {
+            // 娣诲姞绛惧悕
+            String fullContent = smsConfig.getSignName() + content;
+            
+            JSONObject result = SmsUtils.sendSms(
+                    smsConfig.getAddress(),
+                    smsConfig.getUserName(),
+                    smsConfig.getPassword(),
+                    phone,
+                    fullContent
+            );
+
+            if (result != null) {
+                int code = result.getIntValue("code", -1);
+                if (code == 0) {
+                    log.info("鐭俊鍙戦�佹垚鍔燂紝phone={}", phone);
+                    return true;
+                } else {
+                    log.error("鐭俊鍙戦�佸け璐ワ紝phone={}锛宑ode={}锛宮sg={}", 
+                            phone, code, result.getString("msg"));
+                }
+            }
+        } catch (Exception e) {
+            log.error("鐭俊鍙戦�佸紓甯革紝phone={}", phone, e);
+        }
+
+        return false;
+    }
+
+    /**
+     * 鎵归噺鍙戦�佺煭淇�
+     * 
+     * @param phones 鎵嬫満鍙峰垪琛�
+     * @param content 鐭俊鍐呭
+     * @return 鎴愬姛鍙戦�佺殑鏁伴噺
+     */
+    @Override
+    public int sendSmsBatch(List<String> phones, String content) {
+        if (!isEnabled()) {
+            log.info("鐭俊鏈嶅姟宸插叧闂紝璺宠繃鎵归噺鍙戦��");
+            return 0;
+        }
+
+        if (phones == null || phones.isEmpty() || StringUtils.isEmpty(content)) {
+            log.warn("鎵归噺鐭俊鍙戦�佸弬鏁颁笉瀹屾暣");
+            return 0;
+        }
+
+        int successCount = 0;
+        for (String phone : phones) {
+            if (sendSms(phone, content)) {
+                successCount++;
+            }
+        }
+
+        log.info("鎵归噺鐭俊鍙戦�佸畬鎴愶紝鎬绘暟={}锛屾垚鍔�={}", phones.size(), successCount);
+        return successCount;
+    }
+
+    /**
+     * 鍙戦�佷换鍔″垎閰嶉�氱煡鐭俊
+     * 
+     * @param taskId 浠诲姟ID
+     * @param userIds 鐢ㄦ埛ID鍒楄〃
+     * @param excludeUserId 鎺掗櫎鐨勭敤鎴稩D锛堝彲閫夛紝濡傚垱寤轰汉锛�
+     * @return 鎴愬姛鍙戦�佺殑鏁伴噺
+     */
+    @Override
+    public int sendTaskAssignSms(Long taskId, List<Long> userIds, Long excludeUserId) {
+        if (!isEnabled()) {
+            log.info("鐭俊鏈嶅姟宸插叧闂紝璺宠繃浠诲姟鍒嗛厤鐭俊鍙戦�侊紝taskId={}", taskId);
+            return 0;
+        }
+
+        if (taskId == null || userIds == null || userIds.isEmpty()) {
+            log.warn("浠诲姟鍒嗛厤鐭俊鍙戦�佸弬鏁颁笉瀹屾暣锛宼askId={}, userIds={}", taskId, userIds);
+            return 0;
+        }
+
+        // 鏌ヨ浠诲姟淇℃伅
+        SysTask task = sysTaskMapper.selectSysTaskByTaskId(taskId);
+        if (task == null) {
+            log.warn("浠诲姟涓嶅瓨鍦紝鏃犳硶鍙戦�佺煭淇★紝taskId={}", taskId);
+            return 0;
+        }
+
+        // 鏌ヨ鎬ユ晳鎵╁睍淇℃伅
+        SysTaskEmergency emergency = sysTaskEmergencyMapper.selectSysTaskEmergencyByTaskId(taskId);
+
+        // 鏋勫缓鐭俊鍐呭
+        String content = buildTaskAssignSmsContent(task, emergency);
+
+        int successCount = 0;
+        for (Long userId : userIds) {
+            // 鎺掗櫎鎸囧畾鐢ㄦ埛
+            if (excludeUserId != null && excludeUserId.equals(userId)) {
+                log.debug("璺宠繃鎺掗櫎鐢ㄦ埛锛寀serId={}", userId);
+                continue;
+            }
+
+            // 鏌ヨ鐢ㄦ埛淇℃伅
+            SysUser user = sysUserMapper.selectUserById(userId);
+            if (user == null) {
+                log.warn("鐢ㄦ埛涓嶅瓨鍦紝璺宠繃鍙戦�侊紝userId={}", userId);
+                continue;
+            }
+
+            String phone = user.getPhonenumber();
+            if (StringUtils.isEmpty(phone)) {
+                log.warn("鐢ㄦ埛鎵嬫満鍙蜂负绌猴紝璺宠繃鍙戦�侊紝userId={}, userName={}", userId, user.getNickName());
+                continue;
+            }
+
+            // 鍙戦�佺煭淇�
+            if (sendSms(phone, content)) {
+                successCount++;
+                log.info("浠诲姟鍒嗛厤鐭俊鍙戦�佹垚鍔燂紝taskId={}锛寀serId={}锛宲hone={}", taskId, userId, phone);
+            }
+        }
+
+        log.info("浠诲姟鍒嗛厤鐭俊鍙戦�佸畬鎴愶紝taskId={}锛屾�绘暟={}锛屾垚鍔�={}", taskId, userIds.size(), successCount);
+        return successCount;
+    }
+
+    /**
+     * 鍙戦�佷换鍔″垎閰嶉�氱煡鐭俊锛堜笉鎺掗櫎浠讳綍鐢ㄦ埛锛�
+     * 
+     * @param taskId 浠诲姟ID
+     * @param userIds 鐢ㄦ埛ID鍒楄〃
+     * @return 鎴愬姛鍙戦�佺殑鏁伴噺
+     */
+    @Override
+    public int sendTaskAssignSms(Long taskId, List<Long> userIds) {
+        return sendTaskAssignSms(taskId, userIds, null);
+    }
+
+    /**
+     * 鏋勫缓浠诲姟鍒嗛厤鐭俊鍐呭
+     * 
+     * @param task 浠诲姟淇℃伅
+     * @param emergency 鎬ユ晳鎵╁睍淇℃伅锛堝彲閫夛級
+     * @return 鐭俊鍐呭
+     */
+    private String buildTaskAssignSmsContent(SysTask task, SysTaskEmergency emergency) {
+        String template = smsConfig.getTaskAssignTemplate();
+        
+        // 浠诲姟缂栧彿
+        String taskCode = StringUtils.isNotEmpty(task.getTaskCode()) 
+                ? task.getTaskCode() : String.valueOf(task.getTaskId());
+        
+        // 鍑哄彂鍦�
+        String departure;
+        if (emergency != null && StringUtils.isNotEmpty(emergency.getHospitalOutName())) {
+            departure = emergency.getHospitalOutName();
+        } else if (StringUtils.isNotEmpty(task.getDepartureAddress())) {
+            departure = task.getDepartureAddress();
+        } else {
+            departure = "寰呯‘璁�";
+        }
+        
+        // 鐩殑鍦�
+        String destination;
+        if (emergency != null && StringUtils.isNotEmpty(emergency.getHospitalInName())) {
+            destination = emergency.getHospitalInName();
+        } else if (StringUtils.isNotEmpty(task.getDestinationAddress())) {
+            destination = task.getDestinationAddress();
+        } else {
+            destination = "寰呯‘璁�";
+        }
+        
+        // 闄愬埗鍦板潃闀垮害锛岄伩鍏嶇煭淇¤繃闀�
+        if (departure.length() > 20) {
+            departure = departure.substring(0, 17) + "...";
+        }
+        if (destination.length() > 20) {
+            destination = destination.substring(0, 17) + "...";
+        }
+        
+        // 鏇挎崲妯℃澘鍙橀噺
+        return template
+                .replace("{taskCode}", taskCode)
+                .replace("{departure}", departure)
+                .replace("{destination}", destination);
+    }
+
+    /**
+     * 鏌ヨ鐭俊浣欓
+     * 
+     * @return 浣欓淇℃伅
+     */
+    @Override
+    public String getBalance() {
+        try {
+            JSONObject result = SmsUtils.getBalance(
+                    smsConfig.getAddress(),
+                    smsConfig.getUserName(),
+                    smsConfig.getPassword()
+            );
+
+            if (result != null) {
+                return result.toJSONString();
+            }
+        } catch (Exception e) {
+            log.error("鏌ヨ鐭俊浣欓澶辫触", e);
+        }
+        return "鏌ヨ澶辫触";
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysEmergencyTaskServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysEmergencyTaskServiceImpl.java
new file mode 100644
index 0000000..20fcac3
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysEmergencyTaskServiceImpl.java
@@ -0,0 +1,490 @@
+package com.ruoyi.system.service.impl;
+
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.system.domain.SysTask;
+import com.ruoyi.system.domain.SysTaskEmergency;
+import com.ruoyi.system.domain.enums.TaskStatus;
+import com.ruoyi.system.domain.vo.TaskCreateVO;
+import com.ruoyi.system.domain.vo.TaskUpdateVO;
+import com.ruoyi.system.mapper.SysTaskEmergencyMapper;
+import com.ruoyi.system.mapper.SysTaskMapper;
+import com.ruoyi.system.service.ISysEmergencyTaskService;
+import com.ruoyi.system.service.ISysTaskEmergencyService;
+import com.ruoyi.system.service.IMapService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Map;
+
+@Service
+public class SysEmergencyTaskServiceImpl implements ISysEmergencyTaskService {
+
+    private static final Logger log = LoggerFactory.getLogger(SysEmergencyTaskServiceImpl.class);
+
+    @Autowired
+    private SysTaskEmergencyMapper sysTaskEmergencyMapper;
+
+    @Autowired
+    private SysTaskMapper sysTaskMapper;
+
+    @Autowired(required = false)
+    private ISysTaskEmergencyService sysTaskEmergencyService;
+
+    @Autowired(required = false)
+    private IMapService mapService;
+
+    @Override
+    public void saveEmergencyInfo(Long taskId, String createUserName, TaskCreateVO createVO,
+                                  String serviceOrderId, String dispatchOrderId, String serviceOrdNo) {
+        SysTaskEmergency emergencyInfo = new SysTaskEmergency();
+        emergencyInfo.setTaskId(taskId);
+
+        if (createVO.getPatient() != null) {
+            emergencyInfo.setPatientContact(createVO.getPatient().getContact());
+            emergencyInfo.setPatientPhone(createVO.getPatient().getPhone());
+            emergencyInfo.setPatientName(createVO.getPatient().getName());
+            emergencyInfo.setPatientGender(createVO.getPatient().getGender());
+            emergencyInfo.setPatientIdCard(createVO.getPatient().getIdCard());
+            emergencyInfo.setPatientCondition(createVO.getPatient().getCondition());
+        }
+
+        if (createVO.getHospitalOut() != null) {
+            emergencyInfo.setHospitalOutId(createVO.getHospitalOut().getId());
+            emergencyInfo.setHospitalOutName(createVO.getHospitalOut().getName());
+            emergencyInfo.setHospitalOutDepartment(createVO.getHospitalOut().getDepartment());
+            emergencyInfo.setHospitalOutDepartmentId(createVO.getHospitalOut().getDepartmentId());
+            emergencyInfo.setHospitalOutBedNumber(createVO.getHospitalOut().getBedNumber());
+            emergencyInfo.setHospitalOutAddress(createVO.getHospitalOut().getAddress());
+
+            if (createVO.getHospitalOut().getLongitude() != null && createVO.getHospitalOut().getLatitude() != null) {
+                emergencyInfo.setHospitalOutLongitude(createVO.getHospitalOut().getLongitude());
+                emergencyInfo.setHospitalOutLatitude(createVO.getHospitalOut().getLatitude());
+            } else if (mapService != null && createVO.getHospitalOut().getAddress() != null) {
+                try {
+                    Map<String, Double> coords = mapService.geocoding(
+                        createVO.getHospitalOut().getAddress(),
+                        extractCityFromAddress(createVO.getHospitalOut().getAddress())
+                    );
+                    if (coords != null) {
+                        emergencyInfo.setHospitalOutLongitude(BigDecimal.valueOf(coords.get("lng")));
+                        emergencyInfo.setHospitalOutLatitude(BigDecimal.valueOf(coords.get("lat")));
+                        log.info("杞嚭鍖婚櫌GPS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
+                    }
+                } catch (Exception e) {
+                    log.error("鑷姩鑾峰彇杞嚭鍖婚櫌GPS鍧愭爣澶辫触", e);
+                }
+            }
+        }
+
+        if (createVO.getHospitalIn() != null) {
+            emergencyInfo.setHospitalInId(createVO.getHospitalIn().getId());
+            emergencyInfo.setHospitalInName(createVO.getHospitalIn().getName());
+            emergencyInfo.setHospitalInDepartment(createVO.getHospitalIn().getDepartment());
+            emergencyInfo.setHospitalInDepartmentId(createVO.getHospitalIn().getDepartmentId());
+            emergencyInfo.setHospitalInBedNumber(createVO.getHospitalIn().getBedNumber());
+            emergencyInfo.setHospitalInAddress(createVO.getHospitalIn().getAddress());
+
+            if (createVO.getHospitalIn().getLongitude() != null && createVO.getHospitalIn().getLatitude() != null) {
+                emergencyInfo.setHospitalInLongitude(createVO.getHospitalIn().getLongitude());
+                emergencyInfo.setHospitalInLatitude(createVO.getHospitalIn().getLatitude());
+            } else if (mapService != null && createVO.getHospitalIn().getAddress() != null) {
+                try {
+                    Map<String, Double> coords = mapService.geocoding(
+                        createVO.getHospitalIn().getAddress(),
+                        extractCityFromAddress(createVO.getHospitalIn().getAddress())
+                    );
+                    if (coords != null) {
+                        emergencyInfo.setHospitalInLongitude(BigDecimal.valueOf(coords.get("lng")));
+                        emergencyInfo.setHospitalInLatitude(BigDecimal.valueOf(coords.get("lat")));
+                        log.info("杞叆鍖婚櫌GPS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
+                    }
+                } catch (Exception e) {
+                    log.error("鑷姩鑾峰彇杞叆鍖婚櫌GPS鍧愭爣澶辫触", e);
+                }
+            }
+        }
+
+        emergencyInfo.setTransferDistance(createVO.getTransferDistance());
+        emergencyInfo.setTransferPrice(createVO.getPrice());
+        emergencyInfo.setDocumentTypeId(createVO.getDocumentTypeId());
+        emergencyInfo.setTaskTypeId(createVO.getTaskTypeId());
+
+        if (createVO.getDiseaseIds() != null && !createVO.getDiseaseIds().isEmpty()) {
+            String diseaseIdsStr = createVO.getDiseaseIds().stream()
+                .map(String::valueOf)
+                .collect(java.util.stream.Collectors.joining(","));
+            emergencyInfo.setDiseaseIds(diseaseIdsStr);
+        }
+
+        if (serviceOrderId != null) {
+            emergencyInfo.setLegacyServiceOrdId(Long.parseLong(serviceOrderId));
+            emergencyInfo.setSyncStatus(2);
+            emergencyInfo.setSyncTime(DateUtils.getNowDate());
+            emergencyInfo.setSyncErrorMsg("鏃х郴缁熷悓姝ヨ繃鏉�");
+        }
+        if (dispatchOrderId != null) {
+            emergencyInfo.setLegacyDispatchOrdId(Long.parseLong(dispatchOrderId));
+            emergencyInfo.setDispatchSyncStatus(2);
+            emergencyInfo.setDispatchSyncTime(DateUtils.getNowDate());
+            emergencyInfo.setDispatchSyncErrorMsg("鏃х郴缁熷悓姝ヨ繃鏉�");
+        }
+        if (serviceOrdNo != null) {
+            emergencyInfo.setLegacyServiceOrdNo(serviceOrdNo);
+        }
+
+        emergencyInfo.setCreateTime(DateUtils.getNowDate());
+        emergencyInfo.setUpdateTime(DateUtils.getNowDate());
+        emergencyInfo.setCreateBy(createUserName);
+        emergencyInfo.setUpdateBy(createUserName);
+
+        sysTaskEmergencyMapper.insertSysTaskEmergency(emergencyInfo);
+    }
+
+    @Override
+    public void updateEmergencyInfoFromUpdateVO(SysTaskEmergency oldEmergency, TaskUpdateVO updateVO, String userName) {
+        if (oldEmergency == null || updateVO == null) {
+            return;
+        }
+
+        if (updateVO.getPatient() != null) {
+            if (updateVO.getPatient().getContact() != null) {
+                oldEmergency.setPatientContact(updateVO.getPatient().getContact());
+            }
+            if (updateVO.getPatient().getPhone() != null) {
+                oldEmergency.setPatientPhone(updateVO.getPatient().getPhone());
+            }
+            if (updateVO.getPatient().getName() != null) {
+                oldEmergency.setPatientName(updateVO.getPatient().getName());
+            }
+            if (updateVO.getPatient().getGender() != null) {
+                oldEmergency.setPatientGender(updateVO.getPatient().getGender());
+            }
+            if (updateVO.getPatient().getIdCard() != null) {
+                oldEmergency.setPatientIdCard(updateVO.getPatient().getIdCard());
+            }
+            if (updateVO.getPatient().getCondition() != null) {
+                oldEmergency.setPatientCondition(updateVO.getPatient().getCondition());
+            }
+        }
+
+        if (updateVO.getHospitalOut() != null) {
+            if (updateVO.getHospitalOut().getId() != null) {
+                oldEmergency.setHospitalOutId(updateVO.getHospitalOut().getId());
+            }
+            if (updateVO.getHospitalOut().getName() != null) {
+                oldEmergency.setHospitalOutName(updateVO.getHospitalOut().getName());
+            }
+            if (updateVO.getHospitalOut().getDepartment() != null) {
+                oldEmergency.setHospitalOutDepartment(updateVO.getHospitalOut().getDepartment());
+            }
+            if (updateVO.getHospitalOut().getDepartmentId() != null) {
+                oldEmergency.setHospitalOutDepartmentId(updateVO.getHospitalOut().getDepartmentId());
+            }
+            if (updateVO.getHospitalOut().getBedNumber() != null) {
+                oldEmergency.setHospitalOutBedNumber(updateVO.getHospitalOut().getBedNumber());
+            }
+            if (updateVO.getHospitalOut().getAddress() != null
+                    && !updateVO.getHospitalOut().getAddress().equals(oldEmergency.getHospitalOutAddress())) {
+                oldEmergency.setHospitalOutAddress(updateVO.getHospitalOut().getAddress());
+
+                if (updateVO.getHospitalOut().getLongitude() == null
+                        && updateVO.getHospitalOut().getLatitude() == null && mapService != null) {
+                    try {
+                        Map<String, Double> coords = mapService.geocoding(
+                            updateVO.getHospitalOut().getAddress(),
+                            extractCityFromAddress(updateVO.getHospitalOut().getAddress())
+                        );
+                        if (coords != null) {
+                            oldEmergency.setHospitalOutLongitude(BigDecimal.valueOf(coords.get("lng")));
+                            oldEmergency.setHospitalOutLatitude(BigDecimal.valueOf(coords.get("lat")));
+                            log.info("杞嚭鍖婚櫌GPS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
+                        }
+                    } catch (Exception e) {
+                        log.error("鑷姩鑾峰彇杞嚭鍖婚櫌GPS鍧愭爣澶辫触", e);
+                    }
+                }
+            }
+        }
+
+        if (updateVO.getHospitalIn() != null) {
+            if (updateVO.getHospitalIn().getId() != null) {
+                oldEmergency.setHospitalInId(updateVO.getHospitalIn().getId());
+            }
+            if (updateVO.getHospitalIn().getName() != null) {
+                oldEmergency.setHospitalInName(updateVO.getHospitalIn().getName());
+            }
+            if (updateVO.getHospitalIn().getDepartment() != null) {
+                oldEmergency.setHospitalInDepartment(updateVO.getHospitalIn().getDepartment());
+            }
+            if (updateVO.getHospitalIn().getDepartmentId() != null) {
+                oldEmergency.setHospitalInDepartmentId(updateVO.getHospitalIn().getDepartmentId());
+            }
+            if (updateVO.getHospitalIn().getBedNumber() != null) {
+                oldEmergency.setHospitalInBedNumber(updateVO.getHospitalIn().getBedNumber());
+            }
+            if (updateVO.getHospitalIn().getAddress() != null
+                    && !updateVO.getHospitalIn().getAddress().equals(oldEmergency.getHospitalInAddress())) {
+                oldEmergency.setHospitalInAddress(updateVO.getHospitalIn().getAddress());
+
+                if (updateVO.getHospitalIn().getLongitude() == null
+                        && updateVO.getHospitalIn().getLatitude() == null && mapService != null) {
+                    try {
+                        Map<String, Double> coords = mapService.geocoding(
+                            updateVO.getHospitalIn().getAddress(),
+                            extractCityFromAddress(updateVO.getHospitalIn().getAddress())
+                        );
+                        if (coords != null) {
+                            oldEmergency.setHospitalInLongitude(BigDecimal.valueOf(coords.get("lng")));
+                            oldEmergency.setHospitalInLatitude(BigDecimal.valueOf(coords.get("lat")));
+                            log.info("杞叆鍖婚櫌GPS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
+                        }
+                    } catch (Exception e) {
+                        log.error("鑷姩鑾峰彇杞叆鍖婚櫌GPS鍧愭爣澶辫触", e);
+                    }
+                }
+            }
+        }
+
+        if (updateVO.getTransferDistance() != null) {
+            oldEmergency.setTransferDistance(updateVO.getTransferDistance());
+        }
+        if (updateVO.getPrice() != null) {
+            oldEmergency.setTransferPrice(updateVO.getPrice());
+        }
+
+        if (updateVO.getDocumentTypeId() != null) {
+            oldEmergency.setDocumentTypeId(updateVO.getDocumentTypeId());
+        }
+
+        if (updateVO.getTaskTypeId() != null) {
+            oldEmergency.setTaskTypeId(updateVO.getTaskTypeId());
+        }
+
+        if (updateVO.getDiseaseIds() != null && !updateVO.getDiseaseIds().isEmpty()) {
+            String diseaseIdsStr = updateVO.getDiseaseIds().stream()
+                .map(String::valueOf)
+                .collect(java.util.stream.Collectors.joining(","));
+            oldEmergency.setDiseaseIds(diseaseIdsStr);
+        }
+
+        oldEmergency.setUpdateTime(DateUtils.getNowDate());
+        oldEmergency.setUpdateBy(userName);
+
+        sysTaskEmergencyMapper.updateSysTaskEmergency(oldEmergency);
+    }
+
+    @Override
+    public void updateEmergencyInfoFromCreateVO(SysTaskEmergency existingInfo, TaskCreateVO createVO, String userName) {
+        if (existingInfo == null || createVO == null) {
+            return;
+        }
+        
+        log.info("鏇存柊杞繍浠诲姟 taskId:{}", existingInfo.getTaskId());
+        
+        // 鏇存柊鎮h�呬俊鎭�
+        if (createVO.getPatient() != null) {
+            if (createVO.getPatient().getContact() != null) {
+                existingInfo.setPatientContact(createVO.getPatient().getContact());
+            }
+            if (createVO.getPatient().getPhone() != null) {
+                existingInfo.setPatientPhone(createVO.getPatient().getPhone());
+            }
+            if (createVO.getPatient().getName() != null) {
+                existingInfo.setPatientName(createVO.getPatient().getName());
+            }
+            if (createVO.getPatient().getGender() != null) {
+                existingInfo.setPatientGender(createVO.getPatient().getGender());
+            }
+            if (createVO.getPatient().getIdCard() != null) {
+                existingInfo.setPatientIdCard(createVO.getPatient().getIdCard());
+            }
+            if (createVO.getPatient().getCondition() != null) {
+                existingInfo.setPatientCondition(createVO.getPatient().getCondition());
+            }
+        }
+        
+        // 鏇存柊杞嚭鍖婚櫌淇℃伅
+        if (createVO.getHospitalOut() != null) {
+            if (createVO.getHospitalOut().getId() != null) {
+                existingInfo.setHospitalOutId(createVO.getHospitalOut().getId());
+            }
+            if (createVO.getHospitalOut().getName() != null) {
+                existingInfo.setHospitalOutName(createVO.getHospitalOut().getName());
+            }
+            if (createVO.getHospitalOut().getDepartment() != null) {
+                existingInfo.setHospitalOutDepartment(createVO.getHospitalOut().getDepartment());
+            }
+            if (createVO.getHospitalOut().getDepartmentId() != null) {
+                existingInfo.setHospitalOutDepartmentId(createVO.getHospitalOut().getDepartmentId());
+            }
+            if (createVO.getHospitalOut().getBedNumber() != null) {
+                existingInfo.setHospitalOutBedNumber(createVO.getHospitalOut().getBedNumber());
+            }
+            if (createVO.getHospitalOut().getAddress() != null 
+                    && !createVO.getHospitalOut().getAddress().equals(existingInfo.getHospitalOutAddress())) {
+                existingInfo.setHospitalOutAddress(createVO.getHospitalOut().getAddress());
+                
+                // 濡傛灉鏇存柊浜嗗湴鍧�浣嗘病鏈塆PS鍧愭爣锛屽悗绔嚜鍔ㄨ幏鍙�
+                if (createVO.getHospitalOut().getLongitude() == null 
+                        && createVO.getHospitalOut().getLatitude() == null && mapService != null) {
+                    try {
+                        Map<String, Double> coords = mapService.geocoding(
+                            createVO.getHospitalOut().getAddress(),
+                            extractCityFromAddress(createVO.getHospitalOut().getAddress())
+                        );
+                        if (coords != null) {
+                            existingInfo.setHospitalOutLongitude(BigDecimal.valueOf(coords.get("lng")));
+                            existingInfo.setHospitalOutLatitude(BigDecimal.valueOf(coords.get("lat")));
+                            log.info("杞嚭鍖婚櫌GPS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
+                        }
+                    } catch (Exception e) {
+                        log.error("鑷姩鑾峰彇杞嚭鍖婚櫌GPS鍧愭爣澶辫触", e);
+                    }
+                }
+            }
+        }
+        
+        // 鏇存柊杞叆鍖婚櫌淇℃伅
+        if (createVO.getHospitalIn() != null) {
+            if (createVO.getHospitalIn().getId() != null) {
+                existingInfo.setHospitalInId(createVO.getHospitalIn().getId());
+            }
+            if (createVO.getHospitalIn().getName() != null) {
+                existingInfo.setHospitalInName(createVO.getHospitalIn().getName());
+            }
+            if (createVO.getHospitalIn().getDepartment() != null) {
+                existingInfo.setHospitalInDepartment(createVO.getHospitalIn().getDepartment());
+            }
+            if (createVO.getHospitalIn().getDepartmentId() != null) {
+                existingInfo.setHospitalInDepartmentId(createVO.getHospitalIn().getDepartmentId());
+            }
+            if (createVO.getHospitalIn().getBedNumber() != null) {
+                existingInfo.setHospitalInBedNumber(createVO.getHospitalIn().getBedNumber());
+            }
+            if (createVO.getHospitalIn().getAddress() != null 
+                    && !createVO.getHospitalIn().getAddress().equals(existingInfo.getHospitalInAddress())) {
+                existingInfo.setHospitalInAddress(createVO.getHospitalIn().getAddress());
+                
+                // 濡傛灉鏇存柊浜嗗湴鍧�浣嗘病鏈塆PS鍧愭爣锛屽悗绔嚜鍔ㄨ幏鍙�
+                if (createVO.getHospitalIn().getLongitude() == null 
+                        && createVO.getHospitalIn().getLatitude() == null && mapService != null) {
+                    try {
+                        Map<String, Double> coords = mapService.geocoding(
+                            createVO.getHospitalIn().getAddress(),
+                            extractCityFromAddress(createVO.getHospitalIn().getAddress())
+                        );
+                        if (coords != null) {
+                            existingInfo.setHospitalInLongitude(BigDecimal.valueOf(coords.get("lng")));
+                            existingInfo.setHospitalInLatitude(BigDecimal.valueOf(coords.get("lat")));
+                            log.info("杞叆鍖婚櫌GPS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
+                        }
+                    } catch (Exception e) {
+                        log.error("鑷姩鑾峰彇杞叆鍖婚櫌GPS鍧愭爣澶辫触", e);
+                    }
+                }
+            }
+        }
+        
+        // 鏇存柊璐圭敤淇℃伅
+        if (createVO.getTransferDistance() != null) {
+            existingInfo.setTransferDistance(createVO.getTransferDistance());
+        }
+        if (createVO.getPrice() != null) {
+            existingInfo.setTransferPrice(createVO.getPrice());
+        }
+        
+        // 鏇存柊鍗曟嵁绫诲瀷ID
+        if (createVO.getDocumentTypeId() != null) {
+            existingInfo.setDocumentTypeId(createVO.getDocumentTypeId());
+        }
+        
+        // 鏇存柊浠诲姟绫诲瀷ID
+        if (createVO.getTaskTypeId() != null) {
+            existingInfo.setTaskTypeId(createVO.getTaskTypeId());
+        }
+        
+        // 鏇存柊鐥呮儏ID鍒楄〃
+        if (createVO.getDiseaseIds() != null && !createVO.getDiseaseIds().isEmpty()) {
+            String diseaseIdsStr = createVO.getDiseaseIds().stream()
+                .map(String::valueOf)
+                .collect(java.util.stream.Collectors.joining(","));
+            existingInfo.setDiseaseIds(diseaseIdsStr);
+        }
+        
+        // 绯荤粺瀛楁
+        existingInfo.setUpdateTime(DateUtils.getNowDate());
+        existingInfo.setUpdateBy(userName);
+        
+        // 鎵ц鏇存柊
+        sysTaskEmergencyMapper.updateSysTaskEmergency(existingInfo);
+    }
+
+    @Override
+    public void markNeedResyncIfNecessary(Long taskId, SysTask oldTask, TaskUpdateVO updateVO, Boolean updateFromLegacy) {
+        if (!"EMERGENCY_TRANSFER".equals(oldTask.getTaskType()) || Boolean.TRUE.equals(updateFromLegacy)) {
+            return;
+        }
+        try {
+            sysTaskEmergencyService.markNeedResync(taskId);
+        } catch (Exception e) {
+            // 鏍囪澶辫触涓嶅奖鍝嶄富娴佺▼
+        }
+    }
+
+    @Override
+    public boolean hasLegacyServiceOrdId(Long taskId) {
+        SysTask task = sysTaskMapper.selectSysTaskByTaskId(taskId);
+        if (task != null && "EMERGENCY_TRANSFER".equals(task.getTaskType())) {
+            SysTaskEmergency emergencyInfo = sysTaskEmergencyMapper.selectSysTaskEmergencyByTaskId(taskId);
+            return emergencyInfo != null && emergencyInfo.getLegacyServiceOrdId() != null;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean hasLegacyDispatchOrdId(Long taskId) {
+        SysTask task = sysTaskMapper.selectSysTaskByTaskId(taskId);
+        if (task != null && "EMERGENCY_TRANSFER".equals(task.getTaskType())) {
+            SysTaskEmergency emergencyInfo = sysTaskEmergencyMapper.selectSysTaskEmergencyByTaskId(taskId);
+            return emergencyInfo != null && emergencyInfo.getLegacyDispatchOrdId() != null;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean existsByLegacyServiceOrdId(Long legacyServiceOrdId) {
+        if (legacyServiceOrdId == null) {
+            return false;
+        }
+        SysTaskEmergency emergencyInfo = sysTaskEmergencyMapper.selectByLegacyServiceOrdId(legacyServiceOrdId);
+        return emergencyInfo != null;
+    }
+
+    @Override
+    public boolean existsByLegacyDispatchOrdId(Long legacyDispatchOrdId) {
+        if (legacyDispatchOrdId == null) {
+            return false;
+        }
+        SysTaskEmergency emergencyInfo = sysTaskEmergencyMapper.selectByLegacyDispatchOrdId(legacyDispatchOrdId);
+        return emergencyInfo != null;
+    }
+
+    private String extractCityFromAddress(String address) {
+        if (address == null || address.trim().isEmpty()) {
+            return null;
+        }
+        String[] cities = {"骞垮窞", "娣卞湷", "涓滆帪", "浣涘北", "鐝犳捣", "鎯犲窞", "涓北", "姹熼棬", "婀涙睙", "鑲囧簡", "娓呰繙", "闊跺叧", "姊呭窞", "娌虫簮", "娼窞", "鎻槼", "姹曞ご", "姹曞熬", "浜戞诞", "闃虫睙","鍖椾含","涓婃捣","澶╂触"};
+        for (String city : cities) {
+            if (address.contains(city)) {
+                return city;
+            }
+        }
+        return null;
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskAssigneeServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskAssigneeServiceImpl.java
new file mode 100644
index 0000000..1953b3d
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskAssigneeServiceImpl.java
@@ -0,0 +1,260 @@
+package com.ruoyi.system.service.impl;
+
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.system.domain.SysTaskAssignee;
+import com.ruoyi.system.domain.vo.TaskCreateVO;
+import com.ruoyi.system.mapper.SysTaskAssigneeMapper;
+import com.ruoyi.system.service.ISysTaskAssigneeService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 浠诲姟鎵ц浜烘湇鍔″疄鐜扮被
+ * 
+ * @author ruoyi
+ */
+@Slf4j
+@Service
+public class SysTaskAssigneeServiceImpl implements ISysTaskAssigneeService {
+
+    @Autowired
+    private SysTaskAssigneeMapper sysTaskAssigneeMapper;
+
+    @Autowired
+    private ApplicationEventPublisher eventPublisher;
+
+    @Override
+    @Transactional
+    public void saveTaskAssignees(Long taskId, List<TaskCreateVO.AssigneeInfo> assignees, String userName) {
+        if (assignees == null || assignees.isEmpty()) {
+            return;
+        }
+        
+        List<SysTaskAssignee> taskAssignees = new ArrayList<>();
+        Date now = DateUtils.getNowDate();
+        
+        for (int i = 0; i < assignees.size(); i++) {
+            TaskCreateVO.AssigneeInfo assigneeInfo = assignees.get(i);
+            
+            SysTaskAssignee taskAssignee = new SysTaskAssignee();
+            taskAssignee.setTaskId(taskId);
+            taskAssignee.setUserId(assigneeInfo.getUserId());
+            taskAssignee.setUserName(assigneeInfo.getUserName());
+            taskAssignee.setUserType(assigneeInfo.getUserType());
+            // 绗竴涓墽琛屼汉鍛樹负涓昏鎵ц浜�
+            taskAssignee.setIsPrimary(i == 0 ? "1" : "0");
+            taskAssignee.setSortOrder(i);
+            taskAssignee.setCreateTime(now);
+            taskAssignee.setCreateBy(userName);
+            taskAssignee.setUpdateTime(now);
+            taskAssignee.setUpdateBy(userName);
+            
+            taskAssignees.add(taskAssignee);
+        }
+        
+        // 鎵归噺淇濆瓨
+        if (!taskAssignees.isEmpty()) {
+            sysTaskAssigneeMapper.batchInsertSysTaskAssignee(taskAssignees);
+
+        }
+    }
+
+    @Override
+    @Transactional
+    public boolean updateTaskAssignees(Long taskId, List<TaskCreateVO.AssigneeInfo> newAssignees, String userName) {
+        if (newAssignees == null) {
+            return false;
+        }
+        try {
+            List<Long> newAssigneeIds = newAssignees.stream().map(TaskCreateVO.AssigneeInfo::getUserId).collect(Collectors.toList());
+            // 鏌ヨ鐜版湁鐨勬墽琛屼汉鍛�
+            List<SysTaskAssignee> existingAssignees = sysTaskAssigneeMapper.selectSysTaskAssigneeByTaskId(taskId);
+
+            List<Long> existingAssigneeIds = existingAssignees.stream().map(SysTaskAssignee::getUserId).collect(Collectors.toList());
+            // 鎵惧嚭闇�瑕佸垹闄ょ殑鎵ц浜猴紙鍦ㄦ暟鎹簱涓瓨鍦ㄤ絾鍦ㄦ柊鍒楄〃涓笉瀛樺湪锛�
+            List<Long> assigneesToDelete = existingAssignees.stream().map(SysTaskAssignee::getUserId)
+                    .filter(id -> !newAssigneeIds.contains(id))
+                    .collect(Collectors.toList());
+
+            // 鎵惧嚭闇�瑕佹坊鍔犵殑鎵ц浜猴紙鍦ㄦ柊鍒楄〃涓瓨鍦ㄤ絾鍦ㄦ暟鎹簱涓笉瀛樺湪锛�
+            List<TaskCreateVO.AssigneeInfo> assigneesToAdd = newAssignees.stream()
+                    .filter(assignee -> !existingAssigneeIds.contains(assignee.getUserId()))
+                    .collect(Collectors.toList());
+
+
+            boolean hasChanges = !assigneesToDelete.isEmpty() || !assigneesToAdd.isEmpty();
+
+            // 鍙湁鎵ц浜哄憳鍙戠敓鍙樺寲鏃舵墠鏇存柊
+            if (hasChanges) {
+                // 鍒犻櫎涓嶉渶瑕佺殑鎵ц浜哄憳鍏宠仈
+                if (!assigneesToDelete.isEmpty()) {
+                    for (Long userId : assigneesToDelete) {
+                        sysTaskAssigneeMapper.deleteByTaskIdAndUserId(taskId, userId);
+                    }
+                }
+
+                // 娣诲姞鏂扮殑鎵ц浜哄憳鍏宠仈
+                if (!assigneesToAdd.isEmpty()) {
+                    // 涓烘柊澧炵殑鎵ц浜鸿缃帓搴忓拰涓昏鎵ц浜烘爣蹇�
+                    Date now = DateUtils.getNowDate();
+                    List<SysTaskAssignee> newTaskAssignees = new ArrayList<>();
+
+                    for (int i = 0; i < assigneesToAdd.size(); i++) {
+                        TaskCreateVO.AssigneeInfo assigneeInfo = assigneesToAdd.get(i);
+
+                        SysTaskAssignee taskAssignee = new SysTaskAssignee();
+                        taskAssignee.setTaskId(taskId);
+                        taskAssignee.setUserId(assigneeInfo.getUserId());
+                        taskAssignee.setUserName(assigneeInfo.getUserName());
+                        taskAssignee.setUserType(assigneeInfo.getUserType());
+                        // 娉ㄦ剰锛氳繖閲屾垜浠笉鐭ラ亾鏈�缁堢殑鎺掑簭锛屾墍浠ユ殏鏃惰缃负涓�涓緝澶х殑鍊�
+                        // 鍦ㄥ悗缁噸鏂版帓搴忔椂浼氫慨姝�
+                        taskAssignee.setIsPrimary("0"); // 榛樿涓嶆槸涓昏鎵ц浜�
+                        taskAssignee.setSortOrder(999 + i); // 涓存椂鎺掑簭鍊�
+                        taskAssignee.setCreateTime(now);
+                        taskAssignee.setCreateBy(userName);
+                        taskAssignee.setUpdateTime(now);
+                        taskAssignee.setUpdateBy(userName);
+
+                        newTaskAssignees.add(taskAssignee);
+                    }
+
+                    // 鎵归噺淇濆瓨鏂板鐨勬墽琛屼汉
+                    if (!newTaskAssignees.isEmpty()) {
+                        sysTaskAssigneeMapper.batchInsertSysTaskAssignee(newTaskAssignees);
+                    }
+                }
+
+                // 閲嶆柊鏌ヨ鎵�鏈夋墽琛屼汉骞惰缃纭殑鎺掑簭鍜屼富瑕佹墽琛屼汉鏍囧織
+                List<SysTaskAssignee> allAssignees = sysTaskAssigneeMapper.selectSysTaskAssigneeByTaskId(taskId);
+                Map<Long, SysTaskAssignee> allAssigneeMap = allAssignees.stream()
+                        .collect(Collectors.toMap(SysTaskAssignee::getUserId, a -> a));
+
+
+                // 鏇存柊鎵�鏈夋墽琛屼汉鐨勬帓搴忓拰涓昏鎵ц浜烘爣蹇�
+                Date now = DateUtils.getNowDate();
+                for (int i = 0; i < newAssignees.size(); i++) {
+                    TaskCreateVO.AssigneeInfo assigneeInfo = newAssignees.get(i);
+                    SysTaskAssignee taskAssignee = allAssigneeMap.get(assigneeInfo.getUserId());
+
+                    if (taskAssignee != null) {
+                        // 淇濇寔鍘熸湁鐨勭姸鎬佷俊鎭紙濡傚氨缁姸鎬侊級锛屽彧鏇存柊鍩烘湰淇℃伅
+                        taskAssignee.setUserName(assigneeInfo.getUserName());
+                        taskAssignee.setUserType(assigneeInfo.getUserType());
+                        taskAssignee.setIsPrimary(i == 0 ? "1" : "0"); // 绗竴涓负涓昏鎵ц浜�
+                        taskAssignee.setSortOrder(i);
+                        taskAssignee.setUpdateTime(now);
+                        taskAssignee.setUpdateBy(userName);
+
+                        sysTaskAssigneeMapper.updateSysTaskAssignee(taskAssignee);
+                    }
+                }
+
+                return true;
+            }
+        }catch (Exception e){
+            log.error("鎶ラ敊浜嗭紝淇濆瓨鍒版墽琛屼汉鍛樺垎閰�,taskId:{}",taskId,e);
+            throw e;
+        }
+        return false;
+    }
+
+    @Override
+    public List<SysTaskAssignee> getAssigneesByTaskId(Long taskId) {
+        return sysTaskAssigneeMapper.selectSysTaskAssigneeByTaskId(taskId);
+    }
+
+    @Override
+    @Transactional
+    public int deleteAssigneesByTaskId(Long taskId) {
+        return sysTaskAssigneeMapper.deleteSysTaskAssigneeByTaskId(taskId);
+    }
+
+    @Override
+    @Transactional
+    public AjaxResult setAssigneeReady(Long taskId, Long userId) {
+        // 1. 鏌ヨ鎵ц浜哄叧鑱斾俊鎭�
+        List<SysTaskAssignee> assignees = sysTaskAssigneeMapper.selectSysTaskAssigneeByTaskId(taskId);
+        SysTaskAssignee targetAssignee = assignees.stream()
+            .filter(a -> a.getUserId().equals(userId))
+            .findFirst()
+            .orElse(null);
+        
+        if (targetAssignee == null) {
+            return AjaxResult.error("鎮ㄤ笉鏄浠诲姟鐨勬墽琛屼汉");
+        }
+        
+        // 2. 鏇存柊灏辩华鐘舵��
+        targetAssignee.setIsReady("1");
+        targetAssignee.setReadyTime(new Date());
+        targetAssignee.setUpdateBy(SecurityUtils.getUsername());
+        targetAssignee.setUpdateTime(new Date());
+        
+        sysTaskAssigneeMapper.updateSysTaskAssignee(targetAssignee);
+        
+        // 3. 妫�鏌ユ槸鍚︽墍鏈夋墽琛屼汉閮藉凡灏辩华
+        boolean allReady = assignees.stream()
+            .allMatch(a -> a.getUserId().equals(userId) || "1".equals(a.getIsReady()));
+        
+        Map<String, Object> result = new HashMap<>();
+        result.put("allReady", allReady);
+        result.put("message", "灏辩华鎴愬姛");
+        
+        return AjaxResult.success(result);
+    }
+
+    @Override
+    @Transactional
+    public AjaxResult cancelAssigneeReady(Long taskId, Long userId) {
+        // 鏌ヨ鎵ц浜哄叧鑱斾俊鎭�
+        List<SysTaskAssignee> assignees = sysTaskAssigneeMapper.selectSysTaskAssigneeByTaskId(taskId);
+        SysTaskAssignee targetAssignee = assignees.stream()
+            .filter(a -> a.getUserId().equals(userId))
+            .findFirst()
+            .orElse(null);
+        
+        if (targetAssignee == null) {
+            return AjaxResult.error("鎮ㄤ笉鏄浠诲姟鐨勬墽琛屼汉");
+        }
+        
+        // 鏇存柊灏辩华鐘舵��
+        targetAssignee.setIsReady("0");
+        targetAssignee.setReadyTime(null);
+        targetAssignee.setUpdateBy(SecurityUtils.getUsername());
+        targetAssignee.setUpdateTime(new Date());
+        
+        sysTaskAssigneeMapper.updateSysTaskAssignee(targetAssignee);
+        
+        return AjaxResult.success("宸插彇娑堝氨缁�");
+    }
+
+    @Override
+    public List<Long> getAssigneeIds(Long taskId) {
+        List<SysTaskAssignee> assignees = sysTaskAssigneeMapper.selectSysTaskAssigneeByTaskId(taskId);
+        if (assignees == null || assignees.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return assignees.stream()
+            .map(SysTaskAssignee::getUserId)
+            .collect(Collectors.toList());
+    }
+
+    @Override
+    public List<String> getAssigneeNames(Long taskId) {
+        List<SysTaskAssignee> assignees = sysTaskAssigneeMapper.selectSysTaskAssigneeByTaskId(taskId);
+        if (assignees == null || assignees.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return assignees.stream()
+            .map(SysTaskAssignee::getUserName)
+            .collect(Collectors.toList());
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskAttachmentServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskAttachmentServiceImpl.java
new file mode 100644
index 0000000..7c0111f
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskAttachmentServiceImpl.java
@@ -0,0 +1,227 @@
+package com.ruoyi.system.service.impl;
+
+import com.ruoyi.common.config.ImageUrlConfig;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.file.FileUploadUtils;
+import com.ruoyi.common.utils.file.FileUtils;
+import com.ruoyi.system.domain.SysTaskAttachment;
+import com.ruoyi.system.mapper.SysTaskAttachmentMapper;
+import com.ruoyi.system.service.ISysTaskAttachmentService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+
+@Service
+public class SysTaskAttachmentServiceImpl implements ISysTaskAttachmentService {
+
+    @Autowired
+    private SysTaskAttachmentMapper sysTaskAttachmentMapper;
+
+    @Autowired
+    private ImageUrlConfig imageUrlConfig;
+
+    @Override
+    @Transactional
+    public Long uploadAttachment(Long taskId, MultipartFile file, String category) {
+        try {
+            String fileName = category + "_" + System.currentTimeMillis() + "_" + file.getOriginalFilename();
+            String relativePath = saveLocalPath(fileName, file.getInputStream());
+
+            SysTaskAttachment attachment = new SysTaskAttachment();
+            attachment.setTaskId(taskId);
+            attachment.setFileName(file.getOriginalFilename());
+            attachment.setFilePath(relativePath);
+            attachment.setFileSize(file.getSize());
+            attachment.setFileType(getFileType(file.getOriginalFilename()));
+            attachment.setAttachmentCategory(category);
+            attachment.setUploadTime(DateUtils.getNowDate());
+            attachment.setUploadBy(SecurityUtils.getUsername());
+
+            Long result = sysTaskAttachmentMapper.insertSysTaskAttachment(attachment);
+            return result > 0 ? attachment.getAttachmentId() : 0L;
+        } catch (IOException e) {
+            throw new RuntimeException("鏂囦欢涓婁紶澶辫触锛�" + e.getMessage());
+        }
+    }
+
+    @Override
+    @Transactional
+    public Long uploadAttachmentFromWechat(Long taskId, String accessToken, String mediaId, String category) {
+        try {
+            String wechatUrl = String.format(
+                    "https://api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s",
+                    accessToken, mediaId
+            );
+
+            byte[] fileBytes = downloadFromUrl(wechatUrl);
+            if (fileBytes == null || fileBytes.length == 0) {
+                throw new RuntimeException("浠庡井淇′笅杞芥枃浠跺け璐�");
+            }
+
+            String fileName = "wx_" + mediaId.substring(0, Math.min(20, mediaId.length())) + "_" + System.currentTimeMillis() + ".jpg";
+            String relativeFilePath = saveLocalPath(fileName, fileBytes);
+
+            SysTaskAttachment attachment = new SysTaskAttachment();
+            attachment.setTaskId(taskId);
+            attachment.setFileName(fileName);
+            attachment.setFilePath(relativeFilePath);
+            attachment.setFileSize((long) fileBytes.length);
+            attachment.setFileType("jpg");
+            attachment.setAttachmentCategory(category);
+            attachment.setUploadTime(DateUtils.getNowDate());
+            attachment.setUploadBy(SecurityUtils.getUsername());
+
+            Long result = sysTaskAttachmentMapper.insertSysTaskAttachment(attachment);
+            return result > 0 ? attachment.getAttachmentId() : 0L;
+        } catch (Exception e) {
+            throw new RuntimeException("浠庡井淇′笂浼犳枃浠跺け璐ワ細" + e.getMessage());
+        }
+    }
+
+    @Override
+    @Transactional
+    public int deleteAttachment(Long attachmentId) {
+        SysTaskAttachment attachment = sysTaskAttachmentMapper.selectSysTaskAttachmentByAttachmentId(attachmentId);
+        if (attachment == null) {
+            throw new RuntimeException("闄勪欢涓嶅瓨鍦�");
+        }
+
+        try {
+            FileUtils.deleteFile(attachment.getFilePath());
+        } catch (Exception e) {
+            // ignore
+        }
+
+        return sysTaskAttachmentMapper.deleteSysTaskAttachmentByAttachmentId(attachmentId);
+    }
+
+    @Override
+    public SysTaskAttachment getAttachmentById(Long attachmentId) {
+        SysTaskAttachment attachment = sysTaskAttachmentMapper.selectSysTaskAttachmentByAttachmentId(attachmentId);
+        if (attachment != null) {
+            buildAttachmentUrl(attachment);
+        }
+        return attachment;
+    }
+
+    @Override
+    public List<SysTaskAttachment> getAttachmentsByTaskId(Long taskId) {
+        List<SysTaskAttachment> list = sysTaskAttachmentMapper.selectSysTaskAttachmentByTaskId(taskId);
+        if (list != null && !list.isEmpty()) {
+            list.forEach(this::buildAttachmentUrl);
+        }
+        return list;
+    }
+
+    private String saveLocalPath(String fileName, InputStream stream) throws IOException {
+        String baseDir = FileUploadUtils.getDefaultBaseDir();
+        String datePath = DateUtils.datePath();
+        String uploadDir = baseDir + "/task/" + datePath;
+
+        File uploadPath = new File(uploadDir);
+        if (!uploadPath.exists()) {
+            uploadPath.mkdirs();
+        }
+
+        String filePath = uploadDir + "/" + fileName;
+        try (OutputStream os = new FileOutputStream(filePath)) {
+            byte[] buffer = new byte[1024];
+            int bytesRead;
+            while ((bytesRead = stream.read(buffer)) != -1) {
+                os.write(buffer, 0, bytesRead);
+            }
+            os.flush();
+        }
+
+        return "/task/" + datePath + "/" + fileName;
+    }
+
+    private String saveLocalPath(String fileName, byte[] fileBytes) throws IOException {
+        String baseDir = FileUploadUtils.getDefaultBaseDir();
+        String datePath = DateUtils.datePath();
+        String uploadDir = baseDir + "/task/" + datePath;
+
+        File uploadPath = new File(uploadDir);
+        if (!uploadPath.exists()) {
+            uploadPath.mkdirs();
+        }
+
+        String filePath = uploadDir + "/" + fileName;
+        File file = new File(filePath);
+        try (FileOutputStream fos = new FileOutputStream(file)) {
+            fos.write(fileBytes);
+        }
+
+        return "/task/" + datePath + "/" + fileName;
+    }
+
+    private byte[] downloadFromUrl(String fileUrl) throws IOException {
+        URL url = new URL(fileUrl);
+        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        connection.setRequestMethod("GET");
+        connection.setConnectTimeout(10000);
+        connection.setReadTimeout(30000);
+
+        try (InputStream inputStream = connection.getInputStream()) {
+            byte[] buffer = new byte[4096];
+            int bytesRead;
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+            while ((bytesRead = inputStream.read(buffer)) != -1) {
+                outputStream.write(buffer, 0, bytesRead);
+            }
+
+            return outputStream.toByteArray();
+        } finally {
+            connection.disconnect();
+        }
+    }
+
+    private String getFileType(String fileName) {
+        if (StringUtils.isEmpty(fileName)) {
+            return "";
+        }
+        int lastDotIndex = fileName.lastIndexOf(".");
+        if (lastDotIndex > 0 && lastDotIndex < fileName.length() - 1) {
+            return fileName.substring(lastDotIndex + 1).toLowerCase();
+        }
+        return "";
+    }
+
+    @Override
+    public void buildAttachmentUrl(SysTaskAttachment attachment) {
+        if (attachment == null || StringUtils.isEmpty(attachment.getFilePath())) {
+            return;
+        }
+        String baseUrl = imageUrlConfig.getImageUrl();
+        if (!attachment.getFilePath().startsWith("http")) {
+            attachment.setFileUrl(baseUrl + attachment.getFilePath());
+        } else {
+            attachment.setFileUrl(attachment.getFilePath());
+        }
+    }
+
+    @Override
+    public String getCategoryDesc(String category) {
+        if (category == null || category.isEmpty()) {
+            return "鏈垎绫�";
+        }
+        switch (category) {
+            case "1": return "鐭ユ儏鍚屾剰涔�";
+            case "2": return "鐥呬汉璧勬枡";
+            case "3": return "鎿嶄綔璁板綍";
+            case "4": return "鍑鸿溅鍓�";
+            case "5": return "鍑鸿溅鍚�";
+            case "6": return "绯诲畨鍏ㄥ甫";
+            default: return "鍏朵粬";
+        }
+    }
+}
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 a6bca50..d9c04d5 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
@@ -7,9 +7,11 @@
 import java.net.HttpURLConnection;
 import java.net.URL;
 
+import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.system.domain.vo.*;
 import com.ruoyi.system.mapper.*;
+import com.ruoyi.system.service.*;
 import com.ruoyi.system.utils.TaskCodeGenerator;
 import com.ruoyi.common.config.ImageUrlConfig;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -31,12 +33,6 @@
 import com.ruoyi.system.domain.SysTaskAssignee;
 import com.ruoyi.system.domain.enums.TaskStatus;
 import com.ruoyi.system.domain.VehicleInfo;
-import com.ruoyi.system.service.ISysTaskService;
-import com.ruoyi.system.service.ILegacySystemSyncService;
-import com.ruoyi.system.service.ISysTaskEmergencyService;
-import com.ruoyi.system.service.ITaskAttachmentSyncService;
-import com.ruoyi.system.service.IMapService;
-import com.ruoyi.system.service.ISysConfigService;
 import com.ruoyi.system.event.TaskCreatedEvent;
 import com.ruoyi.system.event.TaskAssignedEvent;
 import com.ruoyi.system.event.TaskStatusChangedEvent;
@@ -60,9 +56,6 @@
     private SysTaskMapper sysTaskMapper;
     
     @Autowired
-    private SysTaskVehicleMapper sysTaskVehicleMapper;
-    
-    @Autowired
     private SysTaskAttachmentMapper sysTaskAttachmentMapper;
     
     @Autowired
@@ -72,19 +65,13 @@
     private SysTaskEmergencyMapper sysTaskEmergencyMapper;
     
     @Autowired
-    private SysTaskWelfareMapper sysTaskWelfareMapper;
-    
-    @Autowired
     private SysTaskAssigneeMapper sysTaskAssigneeMapper;
     
-    @Autowired
-    private VehicleInfoMapper vehicleInfoMapper;
-
     @Autowired(required = false)
     private ILegacySystemSyncService legacySystemSyncService;
     
     @Autowired
-    private ISysTaskEmergencyService sysTaskEmergencyService;
+    private ISysEmergencyTaskService sysEmergencyTaskService;
     
     @Autowired
     private ApplicationEventPublisher eventPublisher;
@@ -92,8 +79,8 @@
     @Autowired
     private ImageUrlConfig imageUrlConfig;
     
-    @Autowired(required = false)
-    private ITaskAttachmentSyncService taskAttachmentSyncService;
+    @Autowired
+    private ISysTaskAttachmentService sysTaskAttachmentService;
 
     @Autowired
     private SysUserMapper sysUserMapper;
@@ -103,6 +90,15 @@
 
     @Autowired
     private ISysConfigService configService;
+
+    @Autowired
+    private ISysTaskAssigneeService sysTaskAssigneeService;
+
+    @Autowired
+    private ISysWelfareTaskService sysWelfareTaskService;
+
+    @Autowired
+    private ISysTaskVehicleService sysTaskVehicleService;
 
     /**
      * 鏌ヨ浠诲姟绠$悊
@@ -121,7 +117,7 @@
             }
             // 鍔犺浇绂忕杞︽墿灞曚俊鎭�
             else if ("WELFARE".equals(task.getTaskType())) {
-                SysTaskWelfare welfareInfo = sysTaskWelfareMapper.selectSysTaskWelfareByTaskId(taskId);
+                SysTaskWelfare welfareInfo = sysWelfareTaskService.getWelfareInfoByTaskId(taskId);
                 task.setWelfareInfo(welfareInfo);
             }
         }
@@ -149,6 +145,11 @@
     @Transactional
     public int insertSysTask(TaskCreateVO createVO) {
         String username = SecurityUtils.getUsername();
+        Long userId = SecurityUtils.getUserId();
+        if(userId==null || userId==0){
+            log.error("insertSysTask 鐢ㄦ埛ID涓虹┖ userName:{}",username);
+            return 0;
+        }
         SysTask task = new SysTask();
         task.setTaskCode(generateTaskCode());
         task.setTaskType(createVO.getTaskType());
@@ -157,7 +158,7 @@
         task.setPlannedStartTime(createVO.getPlannedStartTime());
         task.setPlannedEndTime(createVO.getPlannedEndTime());
         task.setAssigneeId(createVO.getAssigneeId());
-        task.setCreatorId(SecurityUtils.getUserId());
+        task.setCreatorId(userId);
         // 浼樺厛浣跨敤鍓嶇浼犲叆鐨勯儴闂↖D锛屽鏋滄病鏈夊垯浣跨敤褰撳墠鐢ㄦ埛鐨勯儴闂↖D
         task.setDeptId(createVO.getDeptId() != null ? createVO.getDeptId() : SecurityUtils.getDeptId());
         task.setCreateBy(username);
@@ -167,123 +168,33 @@
         task.setRemark(createVO.getRemark());
         task.setDelFlag("0");
         
-        // 璁剧疆閫氱敤鍦板潃鍜屽潗鏍囦俊鎭�
-        if (createVO.getDepartureAddress() != null) {
-            task.setDepartureAddress(createVO.getDepartureAddress());
-        }
-        if (createVO.getDestinationAddress() != null) {
-            task.setDestinationAddress(createVO.getDestinationAddress());
-        }
-        if (createVO.getDepartureLongitude() != null) {
-            task.setDepartureLongitude(createVO.getDepartureLongitude());
-        }
-        if (createVO.getDepartureLatitude() != null) {
-            task.setDepartureLatitude(createVO.getDepartureLatitude());
-        }
-        if (createVO.getDestinationLongitude() != null) {
-            task.setDestinationLongitude(createVO.getDestinationLongitude());
-        }
-        if (createVO.getDestinationLatitude() != null) {
-            task.setDestinationLatitude(createVO.getDestinationLatitude());
-        }
-        if (createVO.getEstimatedDistance() != null) {
-            task.setEstimatedDistance(createVO.getEstimatedDistance());
-        }
-        
-        // 璁剧疆鎬ユ晳杞繍鐗瑰畾淇℃伅
-        if (createVO.getTransferTime() != null) {
-            task.setPlannedStartTime(createVO.getTransferTime());
-        }
-        if (createVO.getTransferDistance() != null) {
-            task.setEstimatedDistance(createVO.getTransferDistance());
-        }
-        
-        // 璁剧疆绂忕杞︾壒瀹氫俊鎭�
-        if (createVO.getServiceTime() != null) {
-            task.setPlannedStartTime(createVO.getServiceTime());
-        }
-        if (createVO.getStartAddress() != null) {
-            task.setDepartureAddress(createVO.getStartAddress());
-        }
-        if (createVO.getEndAddress() != null) {
-            task.setDestinationAddress(createVO.getEndAddress());
-        }
-        // 璁剧疆绂忕杞﹀叕閲屾暟
-        if (createVO.getDistance() != null) {
-            task.setEstimatedDistance(createVO.getDistance());
-        }
-        
-        // 鑷姩鑾峰彇鍑哄彂鍦癎PS鍧愭爣锛堝鏋滅己澶憋級
-        if (task.getDepartureAddress() != null && 
-            (task.getDepartureLongitude() == null || task.getDepartureLatitude() == null) && 
-            mapService != null) {
-            try {
-                Map<String, Double> coords = mapService.geocoding(
-                    task.getDepartureAddress(), 
-                    extractCityFromAddress(task.getDepartureAddress())
-                );
-                if (coords != null) {
-                    task.setDepartureLongitude(BigDecimal.valueOf(coords.get("lng")));
-                    task.setDepartureLatitude(BigDecimal.valueOf(coords.get("lat")));
-                    log.info("鍑哄彂鍦癎PS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
-                }
-            } catch (Exception e) {
-                log.error("鑷姩鑾峰彇鍑哄彂鍦癎PS鍧愭爣澶辫触", e);
-            }
-        }
-        
-        // 鑷姩鑾峰彇鐩殑鍦癎PS鍧愭爣锛堝鏋滅己澶憋級
-        if (task.getDestinationAddress() != null && 
-            (task.getDestinationLongitude() == null || task.getDestinationLatitude() == null) && 
-            mapService != null) {
-            try {
-                Map<String, Double> coords = mapService.geocoding(
-                    task.getDestinationAddress(), 
-                    extractCityFromAddress(task.getDestinationAddress())
-                );
-                if (coords != null) {
-                    task.setDestinationLongitude(BigDecimal.valueOf(coords.get("lng")));
-                    task.setDestinationLatitude(BigDecimal.valueOf(coords.get("lat")));
-                    log.info("鐩殑鍦癎PS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
-                }
-            } catch (Exception e) {
-                log.error("鑷姩鑾峰彇鐩殑鍦癎PS鍧愭爣澶辫触", e);
-            }
-        }
+        // 璁剧疆鍦板潃鍜屽潗鏍囦俊鎭�
+        setAddressAndCoordinatesFromVO(task, createVO);
+        // 璁剧疆浠诲姟绫诲瀷鐗瑰畾淇℃伅
+        setTaskTypeSpecificInfo(task, createVO);
+        // 鑷姩濉厖缂哄け鐨凣PS鍧愭爣
+        autoFillMissingGpsCoordinates(task);
         
         int result = sysTaskMapper.insertSysTask(task);
         
         // 淇濆瓨杞﹁締鍏宠仈淇℃伅
         if (result > 0 && createVO.getVehicleIds() != null && !createVO.getVehicleIds().isEmpty()) {
-            for (Long vehicleId : createVO.getVehicleIds()) {
-                SysTaskVehicle taskVehicle = new SysTaskVehicle();
-                taskVehicle.setTaskId(task.getTaskId());
-                taskVehicle.setVehicleId(vehicleId);
-                taskVehicle.setAssignTime(DateUtils.getNowDate());
-                taskVehicle.setAssignBy(username);
-                taskVehicle.setStatus("ASSIGNED");
-                taskVehicle.setCreateBy(username);
-                taskVehicle.setCreateTime(DateUtils.getNowDate());
-                taskVehicle.setUpdateBy(username);
-                taskVehicle.setUpdateTime(DateUtils.getNowDate());
-                
-                sysTaskVehicleMapper.insertSysTaskVehicle(taskVehicle);
-            }
+            sysTaskVehicleService.saveTaskVehicles(task.getTaskId(), createVO.getVehicleIds(), username);
         }
         
         // 淇濆瓨鎵ц浜哄憳淇℃伅锛堝寘鍚鑹茬被鍨嬶級
         if (result > 0 && createVO.getAssignees() != null && !createVO.getAssignees().isEmpty()) {
-            saveTaskAssignees(task.getTaskId(), createVO.getAssignees(),username);
+            sysTaskAssigneeService.saveTaskAssignees(task.getTaskId(), createVO.getAssignees(),username);
         }
         
         // 淇濆瓨鎬ユ晳杞繍鎵╁睍淇℃伅
         if (result > 0 && "EMERGENCY_TRANSFER".equals(createVO.getTaskType())) {
-            saveEmergencyInfo(task.getTaskId(),username, createVO,null,null,null);
+            sysEmergencyTaskService.saveEmergencyInfo(task.getTaskId(), username, createVO, null, null, null);
         }
         
         // 淇濆瓨绂忕杞︽墿灞曚俊鎭�
         if (result > 0 && "WELFARE".equals(createVO.getTaskType())) {
-            saveWelfareInfo(task.getTaskId(),SecurityUtils.getUsername(), createVO);
+            sysWelfareTaskService.saveWelfareInfo(task.getTaskId(), SecurityUtils.getUsername(), createVO);
         }
         
         // 璁板綍鎿嶄綔鏃ュ織
@@ -306,22 +217,7 @@
         
         // 鍙戝竷浠诲姟鍒嗛厤浜嬩欢
         if (result > 0 && createVO.getAssignees() != null && !createVO.getAssignees().isEmpty()) {
-            List<Long> assigneeIds = createVO.getAssignees().stream()
-                .map(assignee -> assignee.getUserId())
-                .collect(Collectors.toList());
-            List<String> assigneeNames = createVO.getAssignees().stream()
-                .map(assignee -> assignee.getUserName())
-                .collect(Collectors.toList());
-            
-            eventPublisher.publishEvent(new TaskAssignedEvent(
-                this,
-                task.getTaskId(),
-                task.getTaskCode(),
-                assigneeIds,
-                assigneeNames,
-                SecurityUtils.getUserId(),
-                SecurityUtils.getUsername()
-            ));
+            this.sendTaskAssigneeEvent(createVO,task,SecurityUtils.getUserId(),SecurityUtils.getUsername());
         }
         
         // 寮傛鍚屾鎬ユ晳杞繍浠诲姟鍒版棫绯荤粺
@@ -380,38 +276,15 @@
 
 
         
-        // 璁剧疆閫氱敤鍦板潃鍜屽潗鏍囦俊鎭�
-        if (createVO.getDepartureAddress() != null) {
-            task.setDepartureAddress(createVO.getDepartureAddress());
-        }
-        if (createVO.getDestinationAddress() != null) {
-            task.setDestinationAddress(createVO.getDestinationAddress());
-        }
-        if (createVO.getDepartureLongitude() != null) {
-            task.setDepartureLongitude(createVO.getDepartureLongitude());
-        }
-        if (createVO.getDepartureLatitude() != null) {
-            task.setDepartureLatitude(createVO.getDepartureLatitude());
-        }
-        if (createVO.getDestinationLongitude() != null) {
-            task.setDestinationLongitude(createVO.getDestinationLongitude());
-        }
-        if (createVO.getDestinationLatitude() != null) {
-            task.setDestinationLatitude(createVO.getDestinationLatitude());
-        }
-        if (createVO.getEstimatedDistance() != null) {
-            task.setEstimatedDistance(createVO.getEstimatedDistance());
-        }
-        
-        // 璁剧疆鎬ユ晳杞繍鐗瑰畾淇℃伅
+        // 璁剧疆鍦板潃鍜屽潗鏍囦俊鎭�
+        setAddressAndCoordinatesFromVO(task, createVO);
+        // 璁剧疆浠诲姟绫诲瀷鐗瑰畾淇℃伅锛堟敞锛歩nsertTask浣跨敤plannedStartTime鑰岄潪serviceTime锛�
         if (createVO.getTransferTime() != null) {
             task.setPlannedStartTime(createVO.getTransferTime());
         }
         if (createVO.getTransferDistance() != null) {
             task.setEstimatedDistance(createVO.getTransferDistance());
         }
-        
-        // 璁剧疆绂忕杞︾壒瀹氫俊鎭�
         if (createVO.getPlannedStartTime() != null) {
             task.setPlannedStartTime(createVO.getPlannedStartTime());
         }
@@ -421,82 +294,33 @@
         if (createVO.getEndAddress() != null) {
             task.setDestinationAddress(createVO.getEndAddress());
         }
-        // 璁剧疆绂忕杞﹀叕閲屾暟
         if (createVO.getDistance() != null) {
             task.setEstimatedDistance(createVO.getDistance());
         }
-        
-        // 鑷姩鑾峰彇鍑哄彂鍦癎PS鍧愭爣锛堝鏋滅己澶憋級
-        if (task.getDepartureAddress() != null && 
-            (task.getDepartureLongitude() == null || task.getDepartureLatitude() == null) && 
-            mapService != null) {
-            try {
-                Map<String, Double> coords = mapService.geocoding(
-                    task.getDepartureAddress(), 
-                    extractCityFromAddress(task.getDepartureAddress())
-                );
-                if (coords != null) {
-                    task.setDepartureLongitude(BigDecimal.valueOf(coords.get("lng")));
-                    task.setDepartureLatitude(BigDecimal.valueOf(coords.get("lat")));
-                    log.info("鍑哄彂鍦癎PS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
-                }
-            } catch (Exception e) {
-                log.error("鑷姩鑾峰彇鍑哄彂鍦癎PS鍧愭爣澶辫触", e);
-            }
-        }
-        
-        // 鑷姩鑾峰彇鐩殑鍦癎PS鍧愭爣锛堝鏋滅己澶憋級
-        if (task.getDestinationAddress() != null && 
-            (task.getDestinationLongitude() == null || task.getDestinationLatitude() == null) && 
-            mapService != null) {
-            try {
-                Map<String, Double> coords = mapService.geocoding(
-                    task.getDestinationAddress(), 
-                    extractCityFromAddress(task.getDestinationAddress())
-                );
-                if (coords != null) {
-                    task.setDestinationLongitude(BigDecimal.valueOf(coords.get("lng")));
-                    task.setDestinationLatitude(BigDecimal.valueOf(coords.get("lat")));
-                    log.info("鐩殑鍦癎PS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
-                }
-            } catch (Exception e) {
-                log.error("鑷姩鑾峰彇鐩殑鍦癎PS鍧愭爣澶辫触", e);
-            }
-        }
+        // 鑷姩濉厖缂哄け鐨凣PS鍧愭爣
+        autoFillMissingGpsCoordinates(task);
         
         int result = sysTaskMapper.insertSysTask(task);
         
         // 淇濆瓨杞﹁締鍏宠仈淇℃伅
         if (result > 0 && createVO.getVehicleIds() != null && !createVO.getVehicleIds().isEmpty()) {
-            for (Long vehicleId : createVO.getVehicleIds()) {
-                SysTaskVehicle taskVehicle = new SysTaskVehicle();
-                taskVehicle.setTaskId(task.getTaskId());
-                taskVehicle.setVehicleId(vehicleId);
-                taskVehicle.setAssignTime(updateTime);
-                taskVehicle.setAssignBy(userName);
-                taskVehicle.setStatus("ASSIGNED");
-                taskVehicle.setCreateBy(userName);
-                taskVehicle.setCreateTime(createTime);
-                taskVehicle.setUpdateBy(userName);
-                taskVehicle.setUpdateTime(updateTime);
-
-                sysTaskVehicleMapper.insertSysTaskVehicle(taskVehicle);
-            }
+            sysTaskVehicleService.saveTaskVehicles(task.getTaskId(), createVO.getVehicleIds(), userName, 
+                updateTime, createTime, updateTime);
         }
         
         // 淇濆瓨鎵ц浜哄憳淇℃伅锛堝寘鍚鑹茬被鍨嬶級
         if (result > 0 && createVO.getAssignees() != null && !createVO.getAssignees().isEmpty()) {
-            saveTaskAssignees(task.getTaskId(), createVO.getAssignees(),userName);
+            sysTaskAssigneeService.saveTaskAssignees(task.getTaskId(), createVO.getAssignees(),userName);
         }
         
         // 淇濆瓨鎬ユ晳杞繍鎵╁睍淇℃伅
         if (result > 0 && "EMERGENCY_TRANSFER".equals(createVO.getTaskType())) {
-            saveEmergencyInfo(task.getTaskId(),userName, createVO, serviceOrderId, dispatchOrderId, serviceOrdNo);
+            sysEmergencyTaskService.saveEmergencyInfo(task.getTaskId(), userName, createVO, serviceOrderId, dispatchOrderId, serviceOrdNo);
         }
         
         // 淇濆瓨绂忕杞︽墿灞曚俊鎭�
         if (result > 0 && "WELFARE".equals(createVO.getTaskType())) {
-            saveWelfareInfo(task.getTaskId(),userName, createVO);
+            sysWelfareTaskService.saveWelfareInfo(task.getTaskId(), userName, createVO);
         }
         
         // 璁板綍鎿嶄綔鏃ュ織
@@ -519,14 +343,23 @@
         
         // 鍙戝竷浠诲姟鍒嗛厤浜嬩欢
         if (result > 0 && createVO.getAssignees() != null && !createVO.getAssignees().isEmpty()) {
-            List<Long> assigneeIds = createVO.getAssignees().stream()
+            this.sendTaskAssigneeEvent(createVO,task,userId,userName);
+        }
+        
+
+        
+        return result;
+    }
+
+    private void sendTaskAssigneeEvent(TaskCreateVO createVO,SysTask task,Long userId,String userName){
+        List<Long> assigneeIds = createVO.getAssignees().stream()
                 .map(assignee -> assignee.getUserId())
                 .collect(Collectors.toList());
-            List<String> assigneeNames = createVO.getAssignees().stream()
+        List<String> assigneeNames = createVO.getAssignees().stream()
                 .map(assignee -> assignee.getUserName())
                 .collect(Collectors.toList());
-            
-            eventPublisher.publishEvent(new TaskAssignedEvent(
+
+        eventPublisher.publishEvent(new TaskAssignedEvent(
                 this,
                 task.getTaskId(),
                 task.getTaskCode(),
@@ -534,12 +367,7 @@
                 assigneeNames,
                 userId,
                 userName
-            ));
-        }
-        
-
-        
-        return result;
+        ));
     }
 
     /**
@@ -555,6 +383,7 @@
         if (oldTask == null) {
             throw new RuntimeException("浠诲姟涓嶅瓨鍦�");
         }
+        Long userId = SecurityUtils.getUserId();
         String userName = SecurityUtils.getUsername();
         
         SysTask task = new SysTask();
@@ -609,7 +438,7 @@
                     if (coords != null) {
                         task.setDepartureLongitude(BigDecimal.valueOf(coords.get("lng")));
                         task.setDepartureLatitude(BigDecimal.valueOf(coords.get("lat")));
-                        log.info("鍑哄彂鍦癎PS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
+//                        log.info("鍑哄彂鍦癎PS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
                     }
                 } catch (Exception e) {
                     log.error("鑷姩鑾峰彇鍑哄彂鍦癎PS鍧愭爣澶辫触", e);
@@ -630,7 +459,7 @@
                     if (coords != null) {
                         task.setDestinationLongitude(BigDecimal.valueOf(coords.get("lng")));
                         task.setDestinationLatitude(BigDecimal.valueOf(coords.get("lat")));
-                        log.info("鐩殑鍦癎PS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
+//                        log.info("鐩殑鍦癎PS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
                     }
                 } catch (Exception e) {
                     log.error("鑷姩鑾峰彇鐩殑鍦癎PS鍧愭爣澶辫触", e);
@@ -638,40 +467,16 @@
             }
         }
         // 鐢ㄤ簬璺熻釜鏄惁闇�瑕侀噸鏂板悓姝ワ紙杞﹁締銆佷汉鍛樸�佸湴鍧�銆佹垚浜や环鍙樻洿锛�
-        boolean needResync = true;
+        boolean needResync = false;
         int result = sysTaskMapper.updateSysTask(task);
         
 
         
         // 鏇存柊杞﹁締鍏宠仈
         if (result > 0 && updateVO.getVehicleIds() != null && !updateVO.getVehicleIds().isEmpty()) {
-            // 鏌ヨ鐜版湁鐨勮溅杈嗗叧鑱�
-            List<SysTaskVehicle> existingVehicles = sysTaskVehicleMapper.selectSysTaskVehicleByTaskId(updateVO.getTaskId());
-            List<Long> existingVehicleIds = existingVehicles.stream()
-                .map(SysTaskVehicle::getVehicleId)
-                .collect(Collectors.toList());
-            
-            // 姣旇緝鏂版棫杞﹁締ID鍒楄〃锛屽垽鏂槸鍚︽湁鍙樺寲
-            boolean vehiclesChanged = !new HashSet<>(existingVehicleIds).equals(new HashSet<>(updateVO.getVehicleIds()));
-            
-            // 鍙湁杞﹁締鍙戠敓鍙樺寲鏃舵墠鏇存柊
+            boolean vehiclesChanged = sysTaskVehicleService.updateTaskVehicles(
+                updateVO.getTaskId(), updateVO.getVehicleIds(), userName);
             if (vehiclesChanged) {
-                // 鍒犻櫎鏃х殑杞﹁締鍏宠仈
-                sysTaskVehicleMapper.deleteSysTaskVehicleByTaskId(updateVO.getTaskId());
-                
-                // 娣诲姞鏂扮殑杞﹁締鍏宠仈
-                Date now = DateUtils.getNowDate();
-                String currentUser = userName;
-                for (Long vehicleId : updateVO.getVehicleIds()) {
-                    SysTaskVehicle taskVehicle = new SysTaskVehicle();
-                    taskVehicle.setTaskId(updateVO.getTaskId());
-                    taskVehicle.setVehicleId(vehicleId);
-                    taskVehicle.setAssignTime(now);
-                    taskVehicle.setAssignBy(currentUser);
-                    taskVehicle.setCreateTime(now);
-                    sysTaskVehicleMapper.insertSysTaskVehicle(taskVehicle);
-                }
-                
                 // 鏍囪闇�瑕侀噸鏂板悓姝ワ紙杞﹁締鍙樻洿锛�
                 needResync = true;
             }
@@ -679,29 +484,9 @@
         
         // 鏇存柊鎵ц浜哄憳锛堟娴嬩汉鍛樺彉鏇达級
         if (result > 0 && updateVO.getAssignees() != null) {
-            // 鏌ヨ鐜版湁鐨勬墽琛屼汉鍛�
-            List<SysTaskAssignee> existingAssignees = sysTaskAssigneeMapper.selectSysTaskAssigneeByTaskId(updateVO.getTaskId());
-            List<Long> existingAssigneeIds = existingAssignees.stream()
-                .map(SysTaskAssignee::getUserId)
-                .collect(Collectors.toList());
-            
-            List<Long> newAssigneeIds = updateVO.getAssignees().stream()
-                .map(TaskCreateVO.AssigneeInfo::getUserId)
-                .collect(Collectors.toList());
-            
-            // 姣旇緝鏂版棫鎵ц浜哄憳ID鍒楄〃锛屽垽鏂槸鍚︽湁鍙樺寲
-            boolean assigneesChanged = !new HashSet<>(existingAssigneeIds).equals(new HashSet<>(newAssigneeIds));
-            
-            // 鍙湁鎵ц浜哄憳鍙戠敓鍙樺寲鏃舵墠鏇存柊
+            boolean assigneesChanged = sysTaskAssigneeService.updateTaskAssignees(
+                updateVO.getTaskId(), updateVO.getAssignees(), userName);
             if (assigneesChanged) {
-                // 鍒犻櫎鏃х殑鎵ц浜哄憳鍏宠仈
-                sysTaskAssigneeMapper.deleteSysTaskAssigneeByTaskId(updateVO.getTaskId());
-                
-                // 娣诲姞鏂扮殑鎵ц浜哄憳鍏宠仈
-                if (!updateVO.getAssignees().isEmpty()) {
-                    saveTaskAssignees(updateVO.getTaskId(), updateVO.getAssignees(), userName);
-                }
-                
                 // 鏍囪闇�瑕侀噸鏂板悓姝ワ紙浜哄憳鍙樻洿锛�
                 needResync = true;
             }
@@ -709,70 +494,35 @@
         
         // 鏇存柊鎬ユ晳杞繍鎵╁睍淇℃伅锛堟娴嬪湴鍧�鍜屾垚浜や环鍙樻洿锛�
         if (result > 0 && "EMERGENCY_TRANSFER".equals(oldTask.getTaskType())) {
-            // 鑾峰彇鏃х殑鎬ユ晳杞繍淇℃伅
             SysTaskEmergency oldEmergency = sysTaskEmergencyMapper.selectSysTaskEmergencyByTaskId(updateVO.getTaskId());
-            
-            // 妫�娴嬭浆鍑哄尰闄㈠湴鍧�鍙樻洿
-            boolean hospitalOutAddressChanged = false;
-            if (updateVO.getHospitalOut() != null && updateVO.getHospitalOut().getAddress() != null
-                && oldEmergency != null
-                && !updateVO.getHospitalOut().getAddress().equals(oldEmergency.getHospitalOutAddress())) {
-                hospitalOutAddressChanged = true;
-            }
-            
-            // 妫�娴嬭浆鍏ュ尰闄㈠湴鍧�鍙樻洿
-            boolean hospitalInAddressChanged = false;
-            if (updateVO.getHospitalIn() != null && updateVO.getHospitalIn().getAddress() != null
-                && oldEmergency != null
-                && !updateVO.getHospitalIn().getAddress().equals(oldEmergency.getHospitalInAddress())) {
-                hospitalInAddressChanged = true;
-            }
-            
-            // 妫�娴嬫垚浜や环鍙樻洿
-            boolean transferPriceChanged = false;
-            if (updateVO.getPrice() != null
-                && oldEmergency != null
-                && oldEmergency.getTransferPrice() != null
-                && updateVO.getPrice().compareTo(oldEmergency.getTransferPrice()) != 0) {
-                transferPriceChanged = true;
-            }
-
-            ;
-            
-            // 鏇存柊鎬ユ晳杞繍淇℃伅
-            if (updateVO.getHospitalOut() != null || updateVO.getHospitalIn() != null || updateVO.getPatient() != null) {
-                updateEmergencyInfoFromCreateVO(updateVO.getTaskId(), updateVO, userName);
-            }
-            
-            // 濡傛灉鍦板潃鎴栨垚浜や环鍙戠敓鍙樻洿锛屾爣璁伴渶瑕侀噸鏂板悓姝�
-            if (hospitalOutAddressChanged || hospitalInAddressChanged || transferPriceChanged) {
-                needResync = true;
-            }
+            sysEmergencyTaskService.updateEmergencyInfoFromUpdateVO(oldEmergency, updateVO, userName);
+            sysEmergencyTaskService.markNeedResyncIfNecessary(updateVO.getTaskId(), oldTask, updateVO, updateFromLegacy);
         }
         
         // 鏇存柊绂忕杞︽墿灞曚俊鎭�
         if (result > 0 && "WELFARE".equals(oldTask.getTaskType())) {
             if (updateVO.getPassenger() != null || updateVO.getStartAddress() != null || updateVO.getEndAddress() != null) {
-                updateWelfareInfoFromCreateVO(updateVO.getTaskId(), updateVO, userName);
+                sysWelfareTaskService.updateWelfareInfo(updateVO.getTaskId(), updateVO, userName);
             }
         }
         
         // 濡傛灉鏄�ユ晳杞繍浠诲姟涓旀湁鍙樻洿锛屾爣璁伴渶瑕侀噸鏂板悓姝�
         if (result > 0 && "EMERGENCY_TRANSFER".equals(oldTask.getTaskType()) && needResync && !updateFromLegacy) {
-            try {
-                sysTaskEmergencyService.markNeedResync(updateVO.getTaskId());
-            } catch (Exception e) {
-                // 鏍囪澶辫触涓嶅奖鍝嶄富娴佺▼
-            }
+            sysEmergencyTaskService.markNeedResyncIfNecessary(updateVO.getTaskId(), oldTask, updateVO, updateFromLegacy);
         }
         
         // 璁板綍鎿嶄綔鏃ュ織
         if (result > 0) {
             recordTaskLog(updateVO.getTaskId(), "UPDATE", "鏇存柊浠诲姟",
                 buildTaskDescription(oldTask), buildTaskDescription(task),
-                SecurityUtils.getUserId(), userName);
+                userId, userName);
         }
-        
+
+        if(result > 0 && oldTask.getTaskStatus().equals(TaskStatus.PENDING.getCode()) && updateVO.getAssignees() != null && !updateVO.getAssignees().isEmpty()){
+
+            this.sendTaskAssigneeEvent(updateVO,oldTask,userId,userName);
+        }
+
         return result;
     }
 
@@ -793,15 +543,25 @@
     @Override
     public int updateTask(TaskUpdateVO updateVO, String serviceOrderId, String dispatchOrderId, String serviceOrdNo,
                          Long userId, String userName, Long deptId, Date createTime, Date updateTime) {
+//        log.info("寮�濮嬫洿鏂颁换鍔� ServiceOrdID: {} , dispatchOrdId:{}", serviceOrderId,dispatchOrderId);
         // 閫氳繃鏃х郴缁熸湇鍔″崟ID鏌ユ壘浠诲姟
         SysTaskEmergency taskEmergency = sysTaskEmergencyMapper.selectByLegacyServiceOrdId(Long.parseLong(serviceOrderId));
         Long taskId = taskEmergency.getTaskId();
         updateVO.setTaskId(taskId);
         SysTask task = new SysTask();
         task.setTaskId(taskId);
+        if(updateVO.getTaskStatus()!=null){
+            task.setTaskStatus(updateVO.getTaskStatus());
+        }
         task.setTaskDescription(updateVO.getTaskDescription());
         task.setPlannedStartTime(updateVO.getPlannedStartTime());
         task.setPlannedEndTime(updateVO.getPlannedEndTime());
+        if(updateVO.getActualStartTime() != null) {
+            task.setActualStartTime(updateVO.getActualStartTime());
+        }
+        if(updateVO.getActualEndTime() != null) {
+            task.setActualEndTime(updateVO.getActualEndTime());
+        }
         task.setAssigneeId(updateVO.getAssigneeId());
         task.setUpdateBy(userName);
         task.setUpdateTime(DateUtils.getNowDate());
@@ -842,7 +602,7 @@
                 if (coords != null) {
                     task.setDepartureLongitude(BigDecimal.valueOf(coords.get("lng")));
                     task.setDepartureLatitude(BigDecimal.valueOf(coords.get("lat")));
-                    log.info("鍑哄彂鍦癎PS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
+//                    log.info("鍑哄彂鍦癎PS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
                 }
             } catch (Exception e) {
                 log.error("鑷姩鑾峰彇鍑哄彂鍦癎PS鍧愭爣澶辫触", e);
@@ -862,7 +622,7 @@
                 if (coords != null) {
                     task.setDestinationLongitude(BigDecimal.valueOf(coords.get("lng")));
                     task.setDestinationLatitude(BigDecimal.valueOf(coords.get("lat")));
-                    log.info("鐩殑鍦癎PS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
+//                    log.info("鐩殑鍦癎PS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
                 }
             } catch (Exception e) {
                 log.error("鑷姩鑾峰彇鐩殑鍦癎PS鍧愭爣澶辫触", e);
@@ -870,62 +630,21 @@
         }
         
         int result = sysTaskMapper.updateSysTask(task);
-        
+//        log.info("鏇存柊杞繍浠诲姟锛孲erviceOrdID:{},dispatchOrderId:{},result:{}",serviceOrderId,dispatchOrderId,result);
+//        log.info("鏇存柊浠诲姟杞﹁締 ServiceOrdID: {} , dispatchOrdId:{},VehicleIds:{}", serviceOrderId,dispatchOrderId,updateVO.getVehicleIds());
         // 鏇存柊杞﹁締鍏宠仈
         if (result > 0 && updateVO.getVehicleIds() != null && !updateVO.getVehicleIds().isEmpty()) {
-            // 鏌ヨ鐜版湁鐨勮溅杈嗗叧鑱�
-            List<SysTaskVehicle> existingVehicles = sysTaskVehicleMapper.selectSysTaskVehicleByTaskId(taskId);
-            List<Long> existingVehicleIds = existingVehicles.stream()
-                .map(SysTaskVehicle::getVehicleId)
-                .collect(Collectors.toList());
-            
-            // 姣旇緝鏂版棫杞﹁締ID鍒楄〃锛屽垽鏂槸鍚︽湁鍙樺寲
-            boolean vehiclesChanged = !new HashSet<>(existingVehicleIds).equals(new HashSet<>(updateVO.getVehicleIds()));
-            
-            // 鍙湁杞﹁締鍙戠敓鍙樺寲鏃舵墠鏇存柊
-            if (vehiclesChanged) {
-                // 鍒犻櫎鏃х殑杞﹁締鍏宠仈
-                sysTaskVehicleMapper.deleteSysTaskVehicleByTaskId(taskId);
-                
-                // 娣诲姞鏂扮殑杞﹁締鍏宠仈
-                Date now = DateUtils.getNowDate();
-                for (Long vehicleId : updateVO.getVehicleIds()) {
-                    SysTaskVehicle taskVehicle = new SysTaskVehicle();
-                    taskVehicle.setTaskId(taskId);
-                    taskVehicle.setVehicleId(vehicleId);
-                    taskVehicle.setAssignTime(now);
-                    taskVehicle.setAssignBy(userName);
-                    taskVehicle.setCreateTime(now);
-                    sysTaskVehicleMapper.insertSysTaskVehicle(taskVehicle);
-                }
-            }
+//            log.info("鏇存柊杞﹁締鍏宠仈 ServiceOrdID:{},dispatchOrderId:{}",serviceOrderId,dispatchOrderId);
+            sysTaskVehicleService.updateTaskVehicles(taskId, updateVO.getVehicleIds(), userName);
         }
-        
+
+        Boolean hasAssignee = updateVO.getAssigneeId() != null && !updateVO.getAssignees().isEmpty() ;
+//        log.info("鏇存柊杞繍浠诲姟锛孲erviceOrdID:{},dispatchOrderId:{},result:{}, hasAssignee:{}",serviceOrderId,dispatchOrderId,result,hasAssignee);
+
         // 鏇存柊鎵ц浜哄憳锛堟娴嬩汉鍛樺彉鏇达級
-        if (result > 0 && updateVO.getAssignees() != null) {
-            // 鏌ヨ鐜版湁鐨勬墽琛屼汉鍛�
-            List<SysTaskAssignee> existingAssignees = sysTaskAssigneeMapper.selectSysTaskAssigneeByTaskId(taskId);
-            List<Long> existingAssigneeIds = existingAssignees.stream()
-                .map(SysTaskAssignee::getUserId)
-                .collect(Collectors.toList());
-            
-            List<Long> newAssigneeIds = updateVO.getAssignees().stream()
-                .map(TaskCreateVO.AssigneeInfo::getUserId)
-                .collect(Collectors.toList());
-            
-            // 姣旇緝鏂版棫鎵ц浜哄憳ID鍒楄〃锛屽垽鏂槸鍚︽湁鍙樺寲
-            boolean assigneesChanged = !new HashSet<>(existingAssigneeIds).equals(new HashSet<>(newAssigneeIds));
-            
-            // 鍙湁鎵ц浜哄憳鍙戠敓鍙樺寲鏃舵墠鏇存柊
-            if (assigneesChanged) {
-                // 鍒犻櫎鏃х殑鎵ц浜哄憳鍏宠仈
-                sysTaskAssigneeMapper.deleteSysTaskAssigneeByTaskId(taskId);
-                
-                // 娣诲姞鏂扮殑鎵ц浜哄憳鍏宠仈
-                if (!updateVO.getAssignees().isEmpty()) {
-                    saveTaskAssignees(taskId, updateVO.getAssignees(), userName);
-                }
-            }
+        if (result > 0 && hasAssignee) {
+//            log.info("鏇存柊鎵ц浜哄憳 ServiceOrdID:{},dispatchOrderId:{}",serviceOrderId,dispatchOrderId);
+            sysTaskAssigneeService.updateTaskAssignees(taskId, updateVO.getAssignees(), userName);
         }
         
         // 鏇存柊鎬ユ晳杞繍鎵╁睍淇℃伅
@@ -936,15 +655,28 @@
             }
             if (dispatchOrderId != null) {
                 taskEmergency.setLegacyDispatchOrdId(Long.parseLong(dispatchOrderId));
+                taskEmergency.setLegacyDispatchOrdId(Long.parseLong(dispatchOrderId));
+                taskEmergency.setDispatchSyncStatus(2);
+                taskEmergency.setDispatchSyncTime(new Date());
+                taskEmergency.setDispatchSyncErrorMsg("鏃х郴缁熷悓姝ヨ繃鏉�");
             }
             if (serviceOrdNo != null) {
                 taskEmergency.setLegacyServiceOrdNo(serviceOrdNo);
             }
-            
+            taskEmergency.setUpdateTime(DateUtils.getNowDate());
+
+            Boolean hasEmergencyInfo = updateVO.getHospitalOut() != null || updateVO.getHospitalIn() != null || updateVO.getPatient() != null;
+//            log.info("鏇存柊杞繍浠诲姟淇℃伅 serviceOrdID:{},dispatchOrderId:{} hasEmergencyInfo:{}",serviceOrderId,
+//                    dispatchOrderId,hasEmergencyInfo);
+
             // 浣跨敤TaskCreateVO鐨勫瓧娈垫潵鏇存柊鎬ユ晳杞繍淇℃伅
-            if (updateVO.getHospitalOut() != null || updateVO.getHospitalIn() != null || updateVO.getPatient() != null) {
-                updateEmergencyInfoFromCreateVO(taskId, updateVO, userName);
+            if (hasEmergencyInfo) {
+                sysEmergencyTaskService.updateEmergencyInfoFromCreateVO(taskEmergency, updateVO, userName);
             }
+        }
+
+        if(updateVO.getTaskStatus()!=null && updateVO.getTaskStatus().equals(TaskStatus.PENDING.getCode()) && updateVO.getAssignees()!=null && !updateVO.getAssignees().isEmpty()){
+            this.sendTaskAssigneeEvent(updateVO,task,userId,userName);
         }
         
         return result;
@@ -1128,39 +860,7 @@
     @Override
     @Transactional
     public Long uploadAttachment(Long taskId, MultipartFile file, String category) {
-        try {
-            // 涓婁紶鏂囦欢锛岃繑鍥炵浉瀵硅矾寰勶紙濡傦細/task/2025/01/15/xxx.jpg锛�
-            String fileName = category+"_"+System.currentTimeMillis()+"_"+file.getOriginalFilename();
-
-            fileName=saveLocalPath(fileName,file.getInputStream());
-            
-            SysTaskAttachment attachment = new SysTaskAttachment();
-            attachment.setTaskId(taskId);
-            attachment.setFileName(file.getOriginalFilename());
-            // 淇濆瓨鐩稿璺緞锛屼笉鍖呭惈 baseDir
-            attachment.setFilePath(fileName);
-            attachment.setFileSize(file.getSize());
-            attachment.setFileType(getFileType(file.getOriginalFilename()));
-            attachment.setAttachmentCategory(category);
-            attachment.setUploadTime(DateUtils.getNowDate());
-            attachment.setUploadBy(SecurityUtils.getUsername());
-            
-            Long result = sysTaskAttachmentMapper.insertSysTaskAttachment(attachment);
-            
-            // 璁板綍鎿嶄綔鏃ュ織
-            if (result > 0) {
-                String categoryDesc = getCategoryDesc(category);
-                recordTaskLog(taskId, "UPDATE", "涓婁紶闄勪欢", null, 
-                             "涓婁紶鏂囦欢锛�" + file.getOriginalFilename() + "(鍒嗙被锛�" + categoryDesc + ")", 
-                             SecurityUtils.getUserId(), SecurityUtils.getUsername());
-
-
-            }
-            
-            return attachment.getAttachmentId();
-        } catch (IOException e) {
-            throw new RuntimeException("鏂囦欢涓婁紶澶辫触锛�" + e.getMessage());
-        }
+        return sysTaskAttachmentService.uploadAttachment(taskId, file, category);
     }
     
     /**
@@ -1175,132 +875,9 @@
     @Override
     @Transactional
     public Long uploadAttachmentFromWechat(Long taskId, String accessToken, String mediaId, String category) {
-        try {
-            // 浠庡井淇℃湇鍔″櫒涓嬭浇鏂囦欢
-            String wechatUrl = String.format(
-                "https://api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s",
-                accessToken, mediaId
-            );
-            
-            byte[] fileBytes = downloadFromUrl(wechatUrl);
-            if (fileBytes == null || fileBytes.length == 0) {
-                throw new RuntimeException("浠庡井淇′笅杞芥枃浠跺け璐�");
-            }
-            
-            // 鐢熸垚鏂囦欢鍚嶏紙浣跨敤mediaId浣滀负鏂囦欢鍚嶇殑涓�閮ㄥ垎锛�
-            String fileName = "wx_" + mediaId.substring(0, Math.min(20, mediaId.length())) + "_" + System.currentTimeMillis() + ".jpg";
-            
-            // 淇濆瓨鍒版湰鍦�
-            String relativeFilePath = saveLocalPath(fileName, fileBytes);
-
-            // 淇濆瓨闄勪欢璁板綍
-            SysTaskAttachment attachment = new SysTaskAttachment();
-            attachment.setTaskId(taskId);
-            attachment.setFileName(fileName);
-            // 淇濆瓨鐩稿璺緞
-            attachment.setFilePath(relativeFilePath);
-            attachment.setFileSize((long) fileBytes.length);
-            attachment.setFileType("jpg");
-            attachment.setAttachmentCategory(category);
-            attachment.setUploadTime(DateUtils.getNowDate());
-            attachment.setUploadBy(SecurityUtils.getUsername());
-            
-            Long result = sysTaskAttachmentMapper.insertSysTaskAttachment(attachment);
-            
-            // 璁板綍鎿嶄綔鏃ュ織
-            if (result > 0) {
-                String categoryDesc = getCategoryDesc(category);
-                recordTaskLog(taskId, "UPDATE", "涓婁紶闄勪欢", null, 
-                             "閫氳繃寰俊涓婁紶鏂囦欢锛�" + fileName + "(鍒嗙被锛�" + categoryDesc + ")", 
-                             SecurityUtils.getUserId(), SecurityUtils.getUsername());
-
-
-            }
-            
-            return attachment.getAttachmentId();
-        } catch (Exception e) {
-            throw new RuntimeException("浠庡井淇′笂浼犳枃浠跺け璐ワ細" + e.getMessage());
-        }
+        return sysTaskAttachmentService.uploadAttachmentFromWechat(taskId, accessToken, mediaId, category);
     }
 
-    private static String saveLocalPath(String fileName, byte[] fileBytes) throws IOException {
-        String baseDir = FileUploadUtils.getDefaultBaseDir();
-        String datePath = DateUtils.datePath();
-        String uploadDir = baseDir + "/task/" + datePath;
-
-        // 鍒涘缓鐩綍
-        File uploadPath = new File(uploadDir);
-        if (!uploadPath.exists()) {
-            uploadPath.mkdirs();
-        }
-
-        // 淇濆瓨鏂囦欢
-        String filePath = uploadDir + "/" + fileName;
-        File file = new File(filePath);
-        try (FileOutputStream fos = new FileOutputStream(file)) {
-            fos.write(fileBytes);
-        }
-
-        // 鐢熸垚鐩稿璺緞锛堜笉鍖呭惈baseDir锛�
-        String relativeFilePath = "/task/" + datePath + "/" + fileName;
-        return relativeFilePath;
-    }
-
-    private String saveLocalPath(String fileName,InputStream stream){
-        String baseDir = FileUploadUtils.getDefaultBaseDir();
-        String datePath = DateUtils.datePath();
-        String uploadDir = baseDir + "/task/" + datePath;
-
-        // 鍒涘缓鐩綍
-        File uploadPath = new File(uploadDir);
-        if (!uploadPath.exists()) {
-            uploadPath.mkdirs();
-        }
-
-        // 淇濆瓨鏂囦欢
-        String filePath = uploadDir + "/" + fileName;
-       //灏唅nputstream鍐欏叆鏂囦欢
-        try (OutputStream os = new FileOutputStream(filePath)) {
-            byte[] buffer = new byte[1024]; // 缂撳啿鍖猴紝鍑忓皯 IO 娆℃暟
-            int bytesRead;
-            // 寰幆璇诲彇杈撳叆娴佷腑鐨勬暟鎹紝鍐欏叆杈撳嚭娴�
-            while ((bytesRead = stream.read(buffer)) != -1) {
-                os.write(buffer, 0, bytesRead);
-            }
-            os.flush(); // 寮哄埗鍒锋柊缂撳啿鍖猴紝纭繚鏁版嵁鍐欏叆鏂囦欢
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-
-        // 鐢熸垚鐩稿璺緞锛堜笉鍖呭惈baseDir锛�
-        String relativeFilePath = "/task/" + datePath + "/" + fileName;
-        return relativeFilePath;
-    }
-
-    /**
-     * 浠� URL 涓嬭浇鏂囦欢
-     */
-    private byte[] downloadFromUrl(String fileUrl) throws IOException {
-        URL url = new URL(fileUrl);
-        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
-        connection.setRequestMethod("GET");
-        connection.setConnectTimeout(10000);
-        connection.setReadTimeout(30000);
-        
-        try (InputStream inputStream = connection.getInputStream()) {
-            byte[] buffer = new byte[4096];
-            int bytesRead;
-            java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();
-            
-            while ((bytesRead = inputStream.read(buffer)) != -1) {
-                outputStream.write(buffer, 0, bytesRead);
-            }
-            
-            return outputStream.toByteArray();
-        } finally {
-            connection.disconnect();
-        }
-    }
 
     /**
      * 鍒犻櫎浠诲姟闄勪欢
@@ -1311,28 +888,7 @@
     @Override
     @Transactional
     public int deleteAttachment(Long attachmentId) {
-        SysTaskAttachment attachment = sysTaskAttachmentMapper.selectSysTaskAttachmentByAttachmentId(attachmentId);
-        if (attachment == null) {
-            throw new RuntimeException("闄勪欢涓嶅瓨鍦�");
-        }
-        
-        // 鍒犻櫎鐗╃悊鏂囦欢
-        try {
-            FileUtils.deleteFile(attachment.getFilePath());
-        } catch (Exception e) {
-            // 蹇界暐鏂囦欢鍒犻櫎澶辫触
-        }
-        
-        int result = sysTaskAttachmentMapper.deleteSysTaskAttachmentByAttachmentId(attachmentId);
-        
-        // 璁板綍鎿嶄綔鏃ュ織
-        if (result > 0) {
-            recordTaskLog(attachment.getTaskId(), "UPDATE", "鍒犻櫎闄勪欢", 
-                         "鍒犻櫎鏂囦欢锛�" + attachment.getFileName(), null, 
-                         SecurityUtils.getUserId(), SecurityUtils.getUsername());
-        }
-        
-        return result;
+        return sysTaskAttachmentService.deleteAttachment(attachmentId);
     }
     
     /**
@@ -1343,17 +899,12 @@
      */
     @Override
     public SysTaskAttachment getAttachmentById(Long attachmentId) {
-        SysTaskAttachment attachment = sysTaskAttachmentMapper.selectSysTaskAttachmentByAttachmentId(attachmentId);
-        if (attachment != null) {
-            // 鎷兼帴瀹屾暣URL
-            buildAttachmentUrl(attachment);
-        }
-        return attachment;
+        return sysTaskAttachmentService.getAttachmentById(attachmentId);
     }
 
     @Override
     public List<SysTaskAttachment> getAttachmentsByTaskId(Long taskId) {
-        return sysTaskAttachmentMapper.selectSysTaskAttachmentByTaskId(taskId);
+        return sysTaskAttachmentService.getAttachmentsByTaskId(taskId);
     }
 
     /**
@@ -1367,21 +918,7 @@
     @Override
     @Transactional
     public int assignVehicleToTask(Long taskId, Long vehicleId, String remark,Long userId,String userName) {
-        // 妫�鏌ユ槸鍚﹀凡缁忓垎閰�
-        int exists = sysTaskVehicleMapper.checkTaskVehicleExists(taskId, vehicleId);
-        if (exists > 0) {
-            throw new RuntimeException("杞﹁締宸茬粡鍒嗛厤缁欒浠诲姟");
-        }
-        
-        SysTaskVehicle taskVehicle = new SysTaskVehicle();
-        taskVehicle.setTaskId(taskId);
-        taskVehicle.setVehicleId(vehicleId);
-        taskVehicle.setAssignTime(DateUtils.getNowDate());
-        taskVehicle.setAssignBy(userName);
-        taskVehicle.setStatus("ASSIGNED");
-        taskVehicle.setRemark(remark);
-        
-        int result = sysTaskVehicleMapper.insertSysTaskVehicle(taskVehicle);
+        int result = sysTaskVehicleService.assignVehicleToTask(taskId, vehicleId, remark, userId, userName);
         
         // 璁板綍鎿嶄綔鏃ュ織
         if (result > 0) {
@@ -1403,7 +940,7 @@
     @Override
     @Transactional
     public int unassignVehicleFromTask(Long taskId, Long vehicleId) {
-        int result = sysTaskVehicleMapper.deleteSysTaskVehicleByTaskIdAndVehicleId(taskId, vehicleId);
+        int result = sysTaskVehicleService.unassignVehicleFromTask(taskId, vehicleId);
         
         // 璁板綍鎿嶄綔鏃ュ織
         if (result > 0) {
@@ -1426,35 +963,13 @@
     @Override
     @Transactional
     public int assignMultipleVehiclesToTask(Long taskId, List<Long> vehicleIds, String remark,Long userId,String userName) {
-        List<SysTaskVehicle> taskVehicles = new ArrayList<>();
-        Date now = DateUtils.getNowDate();
-        String assignBy = userName;
-        
-        for (Long vehicleId : vehicleIds) {
-            // 妫�鏌ユ槸鍚﹀凡缁忓垎閰�
-            int exists = sysTaskVehicleMapper.checkTaskVehicleExists(taskId, vehicleId);
-            if (exists == 0) {
-                SysTaskVehicle taskVehicle = new SysTaskVehicle();
-                taskVehicle.setTaskId(taskId);
-                taskVehicle.setVehicleId(vehicleId);
-                taskVehicle.setAssignTime(now);
-                taskVehicle.setAssignBy(assignBy);
-                taskVehicle.setStatus("ASSIGNED");
-                taskVehicle.setRemark(remark);
-                taskVehicles.add(taskVehicle);
-            }
-        }
-        
-        int result = 0;
-        if (!taskVehicles.isEmpty()) {
-            result = sysTaskVehicleMapper.batchInsertSysTaskVehicle(taskVehicles);
-        }
+        int result = sysTaskVehicleService.assignMultipleVehiclesToTask(taskId, vehicleIds, remark, userId, userName);
         
         // 璁板綍鎿嶄綔鏃ュ織
         if (result > 0) {
             recordTaskLog(taskId, "ASSIGN", "鎵归噺鍒嗛厤杞﹁締", null, 
                          "鍒嗛厤杞﹁締鏁伴噺锛�" + result + "锛屽娉細" + remark, 
-                         userId,userName);
+                         userId, userName);
         }
         
         return result;
@@ -1468,7 +983,7 @@
      */
     @Override
     public List<SysTaskVehicle> getTaskVehicles(Long taskId) {
-        return sysTaskVehicleMapper.selectSysTaskVehicleByTaskId(taskId);
+        return sysTaskVehicleService.getTaskVehicles(taskId);
     }
 
     /**
@@ -1480,34 +995,7 @@
      */
     @Override
     public List<SysTaskVehicle> getAvailableVehicles(Long deptId, String taskType) {
-        List<SysTaskVehicle> availableVehicles = new ArrayList<>();
-        
-        try {
-            // 鏌ヨ鎵�鏈夌姸鎬佷负姝e父鐨勮溅杈嗭紙涓嶉檺鍒堕儴闂級
-            VehicleInfo queryParam = new VehicleInfo();
-            queryParam.setStatus("0"); // 0琛ㄧず姝e父鐘舵��
-            // 涓嶈缃甦eptId锛屾煡璇㈡墍鏈夐儴闂ㄧ殑杞﹁締
-            queryParam.setDeptId(deptId);
-            
-            List<VehicleInfo> vehicles = vehicleInfoMapper.selectVehicleInfoList(queryParam);
-            
-            // 杞崲涓篠ysTaskVehicle瀵硅薄
-            for (VehicleInfo vehicle : vehicles) {
-                SysTaskVehicle taskVehicle = new SysTaskVehicle();
-                taskVehicle.setVehicleId(vehicle.getVehicleId());
-                taskVehicle.setVehicleNo(vehicle.getVehicleNo());
-                taskVehicle.setVehicleType(vehicle.getVehicleType());
-                taskVehicle.setVehicleBrand(vehicle.getVehicleBrand());
-                taskVehicle.setVehicleModel(vehicle.getVehicleModel());
-                taskVehicle.setDeptName(vehicle.getDeptName()); // 娣诲姞閮ㄩ棬鍚嶇О
-                availableVehicles.add(taskVehicle);
-            }
-        } catch (Exception e) {
-            // 濡傛灉鏌ヨ澶辫触锛岃褰曟棩蹇楀苟杩斿洖绌哄垪琛�
-            System.err.println("鏌ヨ鍙敤杞﹁締澶辫触: " + e.getMessage());
-        }
-        
-        return availableVehicles;
+        return sysTaskVehicleService.getAvailableVehicles(deptId, taskType);
     }
 
     /**
@@ -1552,14 +1040,9 @@
         SysTask task = sysTaskMapper.selectSysTaskByTaskId(taskId);
         if (task != null) {
             // 鏌ヨ鍏宠仈杞﹁締
-            task.setAssignedVehicles(sysTaskVehicleMapper.selectSysTaskVehicleByTaskId(taskId));
-            // 鏌ヨ闄勪欢
-            List<SysTaskAttachment> attachments = sysTaskAttachmentMapper.selectSysTaskAttachmentByTaskId(taskId);
-            // 涓烘瘡涓檮浠舵嫾鎺ュ畬鏁碪RL
-            if (attachments != null && !attachments.isEmpty()) {
-                attachments.forEach(this::buildAttachmentUrl);
-            }
-            task.setAttachments(attachments);
+            task.setAssignedVehicles(sysTaskVehicleService.getTaskVehicles(taskId));
+            // 鏌ヨ闄勪欢锛堝凡鑷姩鎷兼帴瀹屾暣URL锛�
+            task.setAttachments(sysTaskAttachmentService.getAttachmentsByTaskId(taskId));
             // 鏌ヨ鎿嶄綔鏃ュ織
             task.setOperationLogs(sysTaskLogMapper.selectSysTaskLogByTaskId(taskId));
             // 鏌ヨ鎵ц浜哄憳鍒楄〃
@@ -1571,7 +1054,7 @@
             }
             // 鍔犺浇绂忕杞︽墿灞曚俊鎭�
             else if ("WELFARE".equals(task.getTaskType())) {
-                SysTaskWelfare welfareInfo = sysTaskWelfareMapper.selectSysTaskWelfareByTaskId(taskId);
+                SysTaskWelfare welfareInfo = sysWelfareTaskService.getWelfareInfoByTaskId(taskId);
                 task.setWelfareInfo(welfareInfo);
             }
         }
@@ -1598,13 +1081,7 @@
      */
     @Override
     public boolean hasLegacyServiceOrdId(Long taskId) {
-        // 鍙湁鎬ユ晳杞繍浠诲姟鎵嶆湁鏃х郴缁烮D
-        SysTask task = sysTaskMapper.selectSysTaskByTaskId(taskId);
-        if (task != null && "EMERGENCY_TRANSFER".equals(task.getTaskType())) {
-            SysTaskEmergency emergencyInfo = sysTaskEmergencyMapper.selectSysTaskEmergencyByTaskId(taskId);
-            return emergencyInfo != null && emergencyInfo.getLegacyServiceOrdId() != null;
-        }
-        return false;
+        return sysEmergencyTaskService.hasLegacyServiceOrdId(taskId);
     }
 
     /**
@@ -1615,13 +1092,7 @@
      */
     @Override
     public boolean hasLegacyDispatchOrdId(Long taskId) {
-        // 鍙湁鎬ユ晳杞繍浠诲姟鎵嶆湁鏃х郴缁烮D
-        SysTask task = sysTaskMapper.selectSysTaskByTaskId(taskId);
-        if (task != null && "EMERGENCY_TRANSFER".equals(task.getTaskType())) {
-            SysTaskEmergency emergencyInfo = sysTaskEmergencyMapper.selectSysTaskEmergencyByTaskId(taskId);
-            return emergencyInfo != null && emergencyInfo.getLegacyDispatchOrdId() != null;
-        }
-        return false;
+        return sysEmergencyTaskService.hasLegacyDispatchOrdId(taskId);
     }
 
     /**
@@ -1632,11 +1103,7 @@
      */
     @Override
     public boolean existsByLegacyServiceOrdId(Long legacyServiceOrdId) {
-        if (legacyServiceOrdId == null) {
-            return false;
-        }
-        SysTaskEmergency emergencyInfo = sysTaskEmergencyMapper.selectByLegacyServiceOrdId(legacyServiceOrdId);
-        return emergencyInfo != null;
+        return sysEmergencyTaskService.existsByLegacyServiceOrdId(legacyServiceOrdId);
     }
 
     /**
@@ -1647,11 +1114,7 @@
      */
     @Override
     public boolean existsByLegacyDispatchOrdId(Long legacyDispatchOrdId) {
-        if (legacyDispatchOrdId == null) {
-            return false;
-        }
-        SysTaskEmergency emergencyInfo = sysTaskEmergencyMapper.selectByLegacyDispatchOrdId(legacyDispatchOrdId);
-        return emergencyInfo != null;
+        return sysEmergencyTaskService.existsByLegacyDispatchOrdId(legacyDispatchOrdId);
     }
 
     @Autowired
@@ -1757,21 +1220,109 @@
         return sb.toString();
     }
 
+
     /**
-     * 鑾峰彇鏂囦欢绫诲瀷
+     * 浠嶵askCreateVO璁剧疆鍦板潃鍜屽潗鏍囦俊鎭埌浠诲姟瀵硅薄
      * 
-     * @param fileName 鏂囦欢鍚�
-     * @return 鏂囦欢绫诲瀷
+     * @param task 浠诲姟瀵硅薄
+     * @param createVO 鍒涘缓VO
      */
-    private String getFileType(String fileName) {
-        if (StringUtils.isEmpty(fileName)) {
-            return "";
+    private void setAddressAndCoordinatesFromVO(SysTask task, TaskCreateVO createVO) {
+        // 璁剧疆閫氱敤鍦板潃鍜屽潗鏍囦俊鎭�
+        if (createVO.getDepartureAddress() != null) {
+            task.setDepartureAddress(createVO.getDepartureAddress());
         }
-        int lastDotIndex = fileName.lastIndexOf(".");
-        if (lastDotIndex > 0 && lastDotIndex < fileName.length() - 1) {
-            return fileName.substring(lastDotIndex + 1).toLowerCase();
+        if (createVO.getDestinationAddress() != null) {
+            task.setDestinationAddress(createVO.getDestinationAddress());
         }
-        return "";
+        if (createVO.getDepartureLongitude() != null) {
+            task.setDepartureLongitude(createVO.getDepartureLongitude());
+        }
+        if (createVO.getDepartureLatitude() != null) {
+            task.setDepartureLatitude(createVO.getDepartureLatitude());
+        }
+        if (createVO.getDestinationLongitude() != null) {
+            task.setDestinationLongitude(createVO.getDestinationLongitude());
+        }
+        if (createVO.getDestinationLatitude() != null) {
+            task.setDestinationLatitude(createVO.getDestinationLatitude());
+        }
+        if (createVO.getEstimatedDistance() != null) {
+            task.setEstimatedDistance(createVO.getEstimatedDistance());
+        }
+    }
+
+    /**
+     * 璁剧疆浠诲姟绫诲瀷鐗瑰畾淇℃伅锛堟�ユ晳杞繍/绂忕杞︼級
+     * 
+     * @param task 浠诲姟瀵硅薄
+     * @param createVO 鍒涘缓VO
+     */
+    private void setTaskTypeSpecificInfo(SysTask task, TaskCreateVO createVO) {
+        // 璁剧疆鎬ユ晳杞繍鐗瑰畾淇℃伅
+        if (createVO.getTransferTime() != null) {
+            task.setPlannedStartTime(createVO.getTransferTime());
+        }
+        if (createVO.getTransferDistance() != null) {
+            task.setEstimatedDistance(createVO.getTransferDistance());
+        }
+        
+        // 璁剧疆绂忕杞︾壒瀹氫俊鎭�
+        if (createVO.getServiceTime() != null) {
+            task.setPlannedStartTime(createVO.getServiceTime());
+        }
+        if (createVO.getStartAddress() != null) {
+            task.setDepartureAddress(createVO.getStartAddress());
+        }
+        if (createVO.getEndAddress() != null) {
+            task.setDestinationAddress(createVO.getEndAddress());
+        }
+        if (createVO.getDistance() != null) {
+            task.setEstimatedDistance(createVO.getDistance());
+        }
+    }
+
+    /**
+     * 鑷姩濉厖缂哄け鐨凣PS鍧愭爣
+     * 
+     * @param task 浠诲姟瀵硅薄
+     */
+    private void autoFillMissingGpsCoordinates(SysTask task) {
+        // 鑷姩鑾峰彇鍑哄彂鍦癎PS鍧愭爣锛堝鏋滅己澶憋級
+        if (task.getDepartureAddress() != null && 
+            (task.getDepartureLongitude() == null || task.getDepartureLatitude() == null) && 
+            mapService != null) {
+            try {
+                Map<String, Double> coords = mapService.geocoding(
+                    task.getDepartureAddress(), 
+                    extractCityFromAddress(task.getDepartureAddress())
+                );
+                if (coords != null) {
+                    task.setDepartureLongitude(BigDecimal.valueOf(coords.get("lng")));
+                    task.setDepartureLatitude(BigDecimal.valueOf(coords.get("lat")));
+                }
+            } catch (Exception e) {
+                log.error("鑷姩鑾峰彇鍑哄彂鍦癎PS鍧愭爣澶辫触", e);
+            }
+        }
+        
+        // 鑷姩鑾峰彇鐩殑鍦癎PS鍧愭爣锛堝鏋滅己澶憋級
+        if (task.getDestinationAddress() != null && 
+            (task.getDestinationLongitude() == null || task.getDestinationLatitude() == null) && 
+            mapService != null) {
+            try {
+                Map<String, Double> coords = mapService.geocoding(
+                    task.getDestinationAddress(), 
+                    extractCityFromAddress(task.getDestinationAddress())
+                );
+                if (coords != null) {
+                    task.setDestinationLongitude(BigDecimal.valueOf(coords.get("lng")));
+                    task.setDestinationLatitude(BigDecimal.valueOf(coords.get("lat")));
+                }
+            } catch (Exception e) {
+                log.error("鑷姩鑾峰彇鐩殑鍦癎PS鍧愭爣澶辫触", e);
+            }
+        }
     }
 
     /**
@@ -1805,473 +1356,6 @@
     }
 
     /**
-     * 淇濆瓨浠诲姟鎵ц浜哄憳淇℃伅锛堝寘鍚鑹茬被鍨嬶級
-     * 
-     * @param taskId 浠诲姟ID
-     * @param assignees 鎵ц浜哄憳淇℃伅鍒楄〃
-     */
-    private void saveTaskAssignees(Long taskId, java.util.List<TaskCreateVO.AssigneeInfo> assignees,String userName) {
-        if (assignees == null || assignees.isEmpty()) {
-            return;
-        }
-        
-        java.util.List<SysTaskAssignee> taskAssignees = new java.util.ArrayList<>();
-        Date now = DateUtils.getNowDate();
-        String currentUser = userName;
-        
-        for (int i = 0; i < assignees.size(); i++) {
-            TaskCreateVO.AssigneeInfo assigneeInfo = assignees.get(i);
-            
-            SysTaskAssignee taskAssignee = new SysTaskAssignee();
-            taskAssignee.setTaskId(taskId);
-            taskAssignee.setUserId(assigneeInfo.getUserId());
-            taskAssignee.setUserName(assigneeInfo.getUserName());
-            taskAssignee.setUserType(assigneeInfo.getUserType());
-            // 绗竴涓墽琛屼汉鍛樹负涓昏鎵ц浜�
-            taskAssignee.setIsPrimary(i == 0 ? "1" : "0");
-            taskAssignee.setSortOrder(i);
-            taskAssignee.setCreateTime(now);
-            taskAssignee.setCreateBy(currentUser);
-            taskAssignee.setUpdateTime(now);
-            taskAssignee.setUpdateBy(currentUser);
-            
-            taskAssignees.add(taskAssignee);
-        }
-        
-        // 鎵归噺淇濆瓨
-        if (!taskAssignees.isEmpty()) {
-            sysTaskAssigneeMapper.batchInsertSysTaskAssignee(taskAssignees);
-        }
-    }
-    
-    /**
-     * 淇濆瓨鎬ユ晳杞繍浠诲姟鎵╁睍淇℃伅
-     * 
-     * @param taskId 浠诲姟ID
-     * @param createVO 浠诲姟鍒涘缓瀵硅薄
-     */
-    private void saveEmergencyInfo(Long taskId,String createUserName, TaskCreateVO createVO,String serviceOrderId,String dispatchOrderId, String serviceOrdNo) {
-        SysTaskEmergency emergencyInfo = new SysTaskEmergency();
-        emergencyInfo.setTaskId(taskId);
-        
-        // 璁剧疆鎮h�呬俊鎭�
-        if (createVO.getPatient() != null) {
-            emergencyInfo.setPatientContact(createVO.getPatient().getContact());
-            emergencyInfo.setPatientPhone(createVO.getPatient().getPhone());
-            emergencyInfo.setPatientName(createVO.getPatient().getName());
-            emergencyInfo.setPatientGender(createVO.getPatient().getGender());
-            emergencyInfo.setPatientIdCard(createVO.getPatient().getIdCard());
-            emergencyInfo.setPatientCondition(createVO.getPatient().getCondition());
-        }
-        
-        // 璁剧疆杞嚭鍖婚櫌淇℃伅
-        if (createVO.getHospitalOut() != null) {
-            emergencyInfo.setHospitalOutId(createVO.getHospitalOut().getId());
-            emergencyInfo.setHospitalOutName(createVO.getHospitalOut().getName());
-            emergencyInfo.setHospitalOutDepartment(createVO.getHospitalOut().getDepartment());
-            emergencyInfo.setHospitalOutDepartmentId(createVO.getHospitalOut().getDepartmentId());
-            emergencyInfo.setHospitalOutBedNumber(createVO.getHospitalOut().getBedNumber());
-            emergencyInfo.setHospitalOutAddress(createVO.getHospitalOut().getAddress());
-            
-            // GPS鍧愭爣锛氫紭鍏堜娇鐢ㄥ墠绔紶鍏ョ殑锛屽惁鍒欏悗绔嚜鍔ㄨ幏鍙�
-            if (createVO.getHospitalOut().getLongitude() != null && createVO.getHospitalOut().getLatitude() != null) {
-                emergencyInfo.setHospitalOutLongitude(createVO.getHospitalOut().getLongitude());
-                emergencyInfo.setHospitalOutLatitude(createVO.getHospitalOut().getLatitude());
-            } else if (mapService != null && createVO.getHospitalOut().getAddress() != null) {
-                // 鍚庣鑷姩鑾峰彇GPS鍧愭爣
-                try {
-                    Map<String, Double> coords = mapService.geocoding(
-                        createVO.getHospitalOut().getAddress(), 
-                        extractCityFromAddress(createVO.getHospitalOut().getAddress())
-                    );
-                    if (coords != null) {
-                        emergencyInfo.setHospitalOutLongitude(BigDecimal.valueOf(coords.get("lng")));
-                        emergencyInfo.setHospitalOutLatitude(BigDecimal.valueOf(coords.get("lat")));
-                        log.info("杞嚭鍖婚櫌GPS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
-                    }
-                } catch (Exception e) {
-                    log.error("鑷姩鑾峰彇杞嚭鍖婚櫌GPS鍧愭爣澶辫触", e);
-                }
-            }
-        }
-        
-        // 璁剧疆杞叆鍖婚櫌淇℃伅
-        if (createVO.getHospitalIn() != null) {
-            emergencyInfo.setHospitalInId(createVO.getHospitalIn().getId());
-            emergencyInfo.setHospitalInName(createVO.getHospitalIn().getName());
-            emergencyInfo.setHospitalInDepartment(createVO.getHospitalIn().getDepartment());
-            emergencyInfo.setHospitalInDepartmentId(createVO.getHospitalIn().getDepartmentId());
-            emergencyInfo.setHospitalInBedNumber(createVO.getHospitalIn().getBedNumber());
-            emergencyInfo.setHospitalInAddress(createVO.getHospitalIn().getAddress());
-            
-            // GPS鍧愭爣锛氫紭鍏堜娇鐢ㄥ墠绔紶鍏ョ殑锛屽惁鍒欏悗绔嚜鍔ㄨ幏鍙�
-            if (createVO.getHospitalIn().getLongitude() != null && createVO.getHospitalIn().getLatitude() != null) {
-                emergencyInfo.setHospitalInLongitude(createVO.getHospitalIn().getLongitude());
-                emergencyInfo.setHospitalInLatitude(createVO.getHospitalIn().getLatitude());
-            } else if (mapService != null && createVO.getHospitalIn().getAddress() != null) {
-                // 鍚庣鑷姩鑾峰彇GPS鍧愭爣
-                try {
-                    Map<String, Double> coords = mapService.geocoding(
-                        createVO.getHospitalIn().getAddress(), 
-                        extractCityFromAddress(createVO.getHospitalIn().getAddress())
-                    );
-                    if (coords != null) {
-                        emergencyInfo.setHospitalInLongitude(BigDecimal.valueOf(coords.get("lng")));
-                        emergencyInfo.setHospitalInLatitude(BigDecimal.valueOf(coords.get("lat")));
-                        log.info("杞叆鍖婚櫌GPS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
-                    }
-                } catch (Exception e) {
-                    log.error("鑷姩鑾峰彇杞叆鍖婚櫌GPS鍧愭爣澶辫触", e);
-                    }
-                }
-        }
-        
-        // 璁剧疆璐圭敤淇℃伅
-        emergencyInfo.setTransferDistance(createVO.getTransferDistance());
-        emergencyInfo.setTransferPrice(createVO.getPrice());
-        
-        // 璁剧疆鍗曟嵁绫诲瀷ID
-        emergencyInfo.setDocumentTypeId(createVO.getDocumentTypeId());
-        
-        // 璁剧疆浠诲姟绫诲瀷ID
-        emergencyInfo.setTaskTypeId(createVO.getTaskTypeId());
-        
-        // 璁剧疆鐥呮儏ID鍒楄〃锛堝皢List<Long>杞崲涓洪�楀彿鍒嗛殧鐨勫瓧绗︿覆锛�
-        if (createVO.getDiseaseIds() != null && !createVO.getDiseaseIds().isEmpty()) {
-            String diseaseIdsStr = createVO.getDiseaseIds().stream()
-                .map(String::valueOf)
-                .collect(Collectors.joining(","));
-            emergencyInfo.setDiseaseIds(diseaseIdsStr);
-        }
-
-        if(serviceOrderId!=null){
-            emergencyInfo.setLegacyServiceOrdId(Long.parseLong(serviceOrderId));
-            emergencyInfo.setSyncStatus(2);
-            emergencyInfo.setSyncTime(new Date());
-            emergencyInfo.setSyncErrorMsg("鏃х郴缁熷悓姝ヨ繃鏉�");
-        }
-        if(dispatchOrderId!=null){
-            emergencyInfo.setLegacyDispatchOrdId(Long.parseLong(dispatchOrderId));
-            emergencyInfo.setDispatchSyncStatus(2);
-            emergencyInfo.setDispatchSyncTime(new Date());
-            emergencyInfo.setDispatchSyncErrorMsg("鏃х郴缁熷悓姝ヨ繃鏉�");
-        }
-        if(serviceOrdNo!=null){
-            emergencyInfo.setLegacyServiceOrdNo(serviceOrdNo);
-        }
-        // 绯荤粺瀛楁
-        emergencyInfo.setCreateTime(DateUtils.getNowDate());
-        emergencyInfo.setUpdateTime(DateUtils.getNowDate());
-        emergencyInfo.setCreateBy(createUserName);
-        emergencyInfo.setUpdateBy(createUserName);
-        
-        sysTaskEmergencyMapper.insertSysTaskEmergency(emergencyInfo);
-    }
-
-    /**
-     * 浠� TaskCreateVO 鏇存柊鎬ユ晳杞繍浠诲姟鎵╁睍淇℃伅
-     * 
-     * @param taskId 浠诲姟ID
-     * @param createVO 浠诲姟鍒涘缓/鏇存柊瀵硅薄
-     * @param userName 鎿嶄綔浜哄悕
-     */
-    private void updateEmergencyInfoFromCreateVO(Long taskId, TaskCreateVO createVO, String userName) {
-        // 鏌ヨ鐜版湁鐨勬墿灞曚俊鎭�
-        SysTaskEmergency existingInfo = sysTaskEmergencyMapper.selectSysTaskEmergencyByTaskId(taskId);
-        if (existingInfo == null) {
-            // 濡傛灉涓嶅瓨鍦紝鍒欏垱寤烘柊鐨�
-            existingInfo = new SysTaskEmergency();
-            existingInfo.setTaskId(taskId);
-            existingInfo.setCreateTime(DateUtils.getNowDate());
-            existingInfo.setCreateBy(userName);
-        }
-        
-        // 鏇存柊鎮h�呬俊鎭�
-        if (createVO.getPatient() != null) {
-            if (createVO.getPatient().getContact() != null) {
-                existingInfo.setPatientContact(createVO.getPatient().getContact());
-            }
-            if (createVO.getPatient().getPhone() != null) {
-                existingInfo.setPatientPhone(createVO.getPatient().getPhone());
-            }
-            if (createVO.getPatient().getName() != null) {
-                existingInfo.setPatientName(createVO.getPatient().getName());
-            }
-            if (createVO.getPatient().getGender() != null) {
-                existingInfo.setPatientGender(createVO.getPatient().getGender());
-            }
-            if (createVO.getPatient().getIdCard() != null) {
-                existingInfo.setPatientIdCard(createVO.getPatient().getIdCard());
-            }
-            if (createVO.getPatient().getCondition() != null) {
-                existingInfo.setPatientCondition(createVO.getPatient().getCondition());
-            }
-        }
-        
-        // 鏇存柊杞嚭鍖婚櫌淇℃伅
-        if (createVO.getHospitalOut() != null) {
-            if (createVO.getHospitalOut().getId() != null) {
-                existingInfo.setHospitalOutId(createVO.getHospitalOut().getId());
-            }
-            if (createVO.getHospitalOut().getName() != null) {
-                existingInfo.setHospitalOutName(createVO.getHospitalOut().getName());
-            }
-            if (createVO.getHospitalOut().getDepartment() != null) {
-                existingInfo.setHospitalOutDepartment(createVO.getHospitalOut().getDepartment());
-            }
-            if (createVO.getHospitalOut().getDepartmentId() != null) {
-                existingInfo.setHospitalOutDepartmentId(createVO.getHospitalOut().getDepartmentId());
-            }
-            if (createVO.getHospitalOut().getBedNumber() != null) {
-                existingInfo.setHospitalOutBedNumber(createVO.getHospitalOut().getBedNumber());
-            }
-            if (createVO.getHospitalOut().getAddress() != null && !createVO.getHospitalOut().getAddress().equals(existingInfo.getHospitalOutAddress())) {
-                existingInfo.setHospitalOutAddress(createVO.getHospitalOut().getAddress());
-                
-                // 濡傛灍鏇存柊浜嗗湴鍧�浣嗘病鏈塆PS鍧愭爣锛屽悗绔嚜鍔ㄨ幏鍙�
-                if (createVO.getHospitalOut().getLongitude() == null && createVO.getHospitalOut().getLatitude() == null && mapService != null) {
-                    try {
-
-                        Map<String, Double> coords = mapService.geocoding(
-                            createVO.getHospitalOut().getAddress(),
-                            extractCityFromAddress(createVO.getHospitalOut().getAddress())
-                        );
-                        if (coords != null) {
-                            existingInfo.setHospitalOutLongitude(BigDecimal.valueOf(coords.get("lng")));
-                            existingInfo.setHospitalOutLatitude(BigDecimal.valueOf(coords.get("lat")));
-                            log.info("杞嚭鍖婚櫌GPS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
-                        }
-                    } catch (Exception e) {
-                        log.error("鑷姩鑾峰彇杞嚭鍖婚櫌GPS鍧愭爣澶辫触", e);
-                    }
-                }
-            }
-
-        }
-        
-        // 鏇存柊杞叆鍖婚櫌淇℃伅
-        if (createVO.getHospitalIn() != null) {
-            if (createVO.getHospitalIn().getId() != null) {
-                existingInfo.setHospitalInId(createVO.getHospitalIn().getId());
-            }
-            if (createVO.getHospitalIn().getName() != null) {
-                existingInfo.setHospitalInName(createVO.getHospitalIn().getName());
-            }
-            if (createVO.getHospitalIn().getDepartment() != null) {
-                existingInfo.setHospitalInDepartment(createVO.getHospitalIn().getDepartment());
-            }
-            if (createVO.getHospitalIn().getDepartmentId() != null) {
-                existingInfo.setHospitalInDepartmentId(createVO.getHospitalIn().getDepartmentId());
-            }
-            if (createVO.getHospitalIn().getBedNumber() != null) {
-                existingInfo.setHospitalInBedNumber(createVO.getHospitalIn().getBedNumber());
-            }
-            if (createVO.getHospitalIn().getAddress() != null && !createVO.getHospitalIn().getAddress().equals(existingInfo.getHospitalInAddress())) {
-                existingInfo.setHospitalInAddress(createVO.getHospitalIn().getAddress());
-                
-                // 濡傛灉鏇存柊浜嗗湴鍧�浣嗘病鏈塆PS鍧愭爣锛屽悗绔嚜鍔ㄨ幏鍙�
-                if (createVO.getHospitalIn().getLongitude() == null && createVO.getHospitalIn().getLatitude() == null && mapService != null) {
-                    try {
-                        Map<String, Double> coords = mapService.geocoding(
-                            createVO.getHospitalIn().getAddress(),
-                            extractCityFromAddress(createVO.getHospitalIn().getAddress())
-                        );
-                        if (coords != null) {
-                            existingInfo.setHospitalInLongitude(BigDecimal.valueOf(coords.get("lng")));
-                            existingInfo.setHospitalInLatitude(BigDecimal.valueOf(coords.get("lat")));
-                            log.info("杞叆鍖婚櫌GPS鍧愭爣鑷姩鑾峰彇鎴愬姛: {}, {}", coords.get("lng"), coords.get("lat"));
-                        }
-                    } catch (Exception e) {
-                        log.error("鑷姩鑾峰彇杞叆鍖婚櫌GPS鍧愭爣澶辫触", e);
-                    }
-                }
-            }
-
-        }
-        
-        // 鏇存柊璐圭敤淇℃伅
-        if (createVO.getTransferDistance() != null) {
-            existingInfo.setTransferDistance(createVO.getTransferDistance());
-        }
-        if (createVO.getPrice() != null) {
-            existingInfo.setTransferPrice(createVO.getPrice());
-        }
-        
-        // 鏇存柊鍗曟嵁绫诲瀷ID
-        if (createVO.getDocumentTypeId() != null) {
-            existingInfo.setDocumentTypeId(createVO.getDocumentTypeId());
-        }
-        
-        // 鏇存柊浠诲姟绫诲瀷ID
-        if (createVO.getTaskTypeId() != null) {
-            existingInfo.setTaskTypeId(createVO.getTaskTypeId());
-        }
-        
-        // 鏇存柊鐥呮儏ID鍒楄〃
-        if (createVO.getDiseaseIds() != null && !createVO.getDiseaseIds().isEmpty()) {
-            String diseaseIdsStr = createVO.getDiseaseIds().stream()
-                .map(String::valueOf)
-                .collect(Collectors.joining(","));
-            existingInfo.setDiseaseIds(diseaseIdsStr);
-        }
-        
-        // 绯荤粺瀛楁
-        existingInfo.setUpdateTime(DateUtils.getNowDate());
-        existingInfo.setUpdateBy(userName);
-        
-        // 鎵ц鏇存柊
-        sysTaskEmergencyMapper.updateSysTaskEmergency(existingInfo);
-    }
-    
-    /**
-     * 浠� TaskCreateVO 鏇存柊绂忕杞︿换鍔℃墿灞曚俊鎭�
-     * 
-     * @param taskId 浠诲姟ID
-     * @param createVO 浠诲姟鍒涘缓/鏇存柊瀵硅薄
-     * @param userName 鎿嶄綔浜哄悕
-     */
-    private void updateWelfareInfoFromCreateVO(Long taskId, TaskCreateVO createVO, String userName) {
-        // 鏌ヨ鐜版湁鐨勬墿灞曚俊鎭�
-        SysTaskWelfare existingInfo = sysTaskWelfareMapper.selectSysTaskWelfareByTaskId(taskId);
-        if (existingInfo == null) {
-            // 濡傛灉涓嶅瓨鍦紝鍒欏垱寤烘柊鐨�
-            existingInfo = new SysTaskWelfare();
-            existingInfo.setTaskId(taskId);
-            existingInfo.setCreateTime(DateUtils.getNowDate());
-            existingInfo.setCreateBy(userName);
-        }
-        
-        // 鏇存柊涔樺淇℃伅
-        if (createVO.getPassenger() != null) {
-            if (createVO.getPassenger().getContact() != null) {
-                existingInfo.setPassengerContact(createVO.getPassenger().getContact());
-            }
-            if (createVO.getPassenger().getPhone() != null) {
-                existingInfo.setPassengerPhone(createVO.getPassenger().getPhone());
-            }
-        }
-        
-        // 鏇存柊鍦板潃淇℃伅
-        if (createVO.getStartAddress() != null) {
-            existingInfo.setPickupAddress(createVO.getStartAddress());
-        }
-        if (createVO.getEndAddress() != null) {
-            existingInfo.setDestinationAddress(createVO.getEndAddress());
-        }
-        
-        // 鏇存柊GPS鍧愭爣
-        if (createVO.getDepartureLongitude() != null) {
-            existingInfo.setPickupLongitude(createVO.getDepartureLongitude());
-        }
-        if (createVO.getDepartureLatitude() != null) {
-            existingInfo.setPickupLatitude(createVO.getDepartureLatitude());
-        }
-        if (createVO.getDestinationLongitude() != null) {
-            existingInfo.setDestinationLongitude(createVO.getDestinationLongitude());
-        }
-        if (createVO.getDestinationLatitude() != null) {
-            existingInfo.setDestinationLatitude(createVO.getDestinationLatitude());
-        }
-        
-        // 鏇存柊璺濈鍜岃垂鐢�
-        if (createVO.getDistance() != null) {
-            existingInfo.setServiceDistance(createVO.getDistance());
-        } else if (createVO.getEstimatedDistance() != null) {
-            existingInfo.setServiceDistance(createVO.getEstimatedDistance());
-        }
-        if (createVO.getPrice() != null) {
-            existingInfo.setServicePrice(createVO.getPrice());
-        }
-        
-        // 绯荤粺瀛楁
-        existingInfo.setUpdateTime(DateUtils.getNowDate());
-        existingInfo.setUpdateBy(userName);
-        
-        // 鎵ц鏇存柊
-        sysTaskWelfareMapper.updateSysTaskWelfare(existingInfo);
-    }
-
-    /**
-     * 淇濆瓨绂忕杞︿换鍔℃墿灞曚俊鎭�
-     * 
-     * @param taskId 浠诲姟ID
-     * @param createVO 浠诲姟鍒涘缓瀵硅薄
-     */
-    private void saveWelfareInfo(Long taskId,String userName, TaskCreateVO createVO) {
-        SysTaskWelfare welfareInfo = new SysTaskWelfare();
-        welfareInfo.setTaskId(taskId);
-        
-        // 璁剧疆涔樺淇℃伅
-        if (createVO.getPassenger() != null) {
-            welfareInfo.setPassengerContact(createVO.getPassenger().getContact());
-            welfareInfo.setPassengerPhone(createVO.getPassenger().getPhone());
-        }
-        
-        // 璁剧疆鍦板潃淇℃伅
-        welfareInfo.setPickupAddress(createVO.getStartAddress());
-        welfareInfo.setDestinationAddress(createVO.getEndAddress());
-        
-        // 璁剧疆GPS鍧愭爣
-        welfareInfo.setPickupLongitude(createVO.getDepartureLongitude());
-        welfareInfo.setPickupLatitude(createVO.getDepartureLatitude());
-        welfareInfo.setDestinationLongitude(createVO.getDestinationLongitude());
-        welfareInfo.setDestinationLatitude(createVO.getDestinationLatitude());
-        
-        // 璁剧疆璺濈鍜岃垂鐢�
-        // 浼樺厛浣跨敤绂忕杞︿笓鐢ㄧ殑distance瀛楁锛屽鏋滄病鏈夊垯浣跨敤閫氱敤鐨別stimatedDistance
-        BigDecimal serviceDistance = createVO.getDistance() != null ? createVO.getDistance() : createVO.getEstimatedDistance();
-        welfareInfo.setServiceDistance(serviceDistance);
-        welfareInfo.setServicePrice(createVO.getPrice());
-        
-        // 绯荤粺瀛楁
-        welfareInfo.setCreateTime(DateUtils.getNowDate());
-        welfareInfo.setUpdateTime(DateUtils.getNowDate());
-        welfareInfo.setCreateBy(userName);
-        welfareInfo.setUpdateBy(userName);
-        
-        sysTaskWelfareMapper.insertSysTaskWelfare(welfareInfo);
-    }
-    
-    /**
-     * 鑾峰彇闄勪欢鍒嗙被鎻忚堪
-     * 
-     * @param category 闄勪欢鍒嗙被浠g爜
-     * @return 鍒嗙被鎻忚堪
-     */
-    private String getCategoryDesc(String category) {
-        if (category == null || category.isEmpty()) {
-            return "鏈垎绫�";
-        }
-        switch (category) {
-            case "1": return "鐭ユ儏鍚屾剰涔�";
-            case "2": return "鐥呬汉璧勬枡";
-            case "3": return "鎿嶄綔璁板綍";
-            case "4": return "鍑鸿溅鍓�";
-            case "5": return "鍑鸿溅鍚�";
-            case "6": return "绯诲畨鍏ㄥ甫";
-            default: return "鍏朵粬";
-        }
-    }
-    
-    /**
-     * 鏋勫缓闄勪欢鐨勫畬鏁碪RL
-     * 
-     * @param attachment 闄勪欢瀵硅薄
-     */
-    private void buildAttachmentUrl(SysTaskAttachment attachment) {
-        if (attachment != null && StringUtils.isNotEmpty(attachment.getFilePath())) {
-            String imageUrl = imageUrlConfig.getImageUrl();
-            if (StringUtils.isNotEmpty(imageUrl)) {
-                // 鎷兼帴瀹屾暣URL锛氬煙鍚� + 鐩稿璺緞
-                attachment.setFileUrl(imageUrl + attachment.getFilePath());
-            } else {
-                // 濡傛灉鏈厤缃煙鍚嶏紝鐩存帴浣跨敤鐩稿璺緞
-                attachment.setFileUrl(attachment.getFilePath());
-            }
-        }
-    }
-    
-    /**
      * 妫�鏌ヤ换鍔℃槸鍚﹀彲浠ュ嚭鍙�
      * 妫�鏌ワ細
      * 1. 杞﹁締鏄惁鏈夋湭瀹屾垚鐨勪换鍔�
@@ -2281,11 +1365,11 @@
      * @return AjaxResult 鏍¢獙缁撴灉
      */
     @Override
-    public com.ruoyi.common.core.domain.AjaxResult checkTaskCanDepart(Long taskId) {
+    public AjaxResult checkTaskCanDepart(Long taskId) {
         // 鑾峰彇浠诲姟璇︽儏
         SysTask task = this.getTaskDetail(taskId);
         if (task == null) {
-            return com.ruoyi.common.core.domain.AjaxResult.error("浠诲姟涓嶅瓨鍦�");
+            return AjaxResult.error("浠诲姟涓嶅瓨鍦�");
         }
         
         List<Map<String, Object>> conflicts = new ArrayList<>();
@@ -2369,7 +1453,7 @@
         result.put("valid", conflicts.isEmpty());
         result.put("conflicts", conflicts);
         
-        return com.ruoyi.common.core.domain.AjaxResult.success(result);
+        return AjaxResult.success(result);
     }
 
     /**
@@ -2381,35 +1465,8 @@
      */
     @Override
     @Transactional
-    public com.ruoyi.common.core.domain.AjaxResult setAssigneeReady(Long taskId, Long userId) {
-        // 1. 鏌ヨ鎵ц浜哄叧鑱斾俊鎭�
-        List<SysTaskAssignee> assignees = sysTaskAssigneeMapper.selectSysTaskAssigneeByTaskId(taskId);
-        SysTaskAssignee targetAssignee = assignees.stream()
-            .filter(a -> a.getUserId().equals(userId))
-            .findFirst()
-            .orElse(null);
-        
-        if (targetAssignee == null) {
-            return com.ruoyi.common.core.domain.AjaxResult.error("鎮ㄤ笉鏄浠诲姟鐨勬墽琛屼汉");
-        }
-        
-        // 2. 鏇存柊灏辩华鐘舵��
-        targetAssignee.setIsReady("1");
-        targetAssignee.setReadyTime(new Date());
-        targetAssignee.setUpdateBy(SecurityUtils.getUsername());
-        targetAssignee.setUpdateTime(new Date());
-        
-        sysTaskAssigneeMapper.updateSysTaskAssignee(targetAssignee);
-        
-        // 3. 妫�鏌ユ槸鍚︽墍鏈夋墽琛屼汉閮藉凡灏辩华
-        boolean allReady = assignees.stream()
-            .allMatch(a -> a.getUserId().equals(userId) || "1".equals(a.getIsReady()));
-        
-        Map<String, Object> result = new HashMap<>();
-        result.put("allReady", allReady);
-        result.put("message", "灏辩华鎴愬姛");
-        
-        return com.ruoyi.common.core.domain.AjaxResult.success(result);
+    public AjaxResult setAssigneeReady(Long taskId, Long userId) {
+        return sysTaskAssigneeService.setAssigneeReady(taskId, userId);
     }
 
     /**
@@ -2421,27 +1478,8 @@
      */
     @Override
     @Transactional
-    public com.ruoyi.common.core.domain.AjaxResult cancelAssigneeReady(Long taskId, Long userId) {
-        // 鏌ヨ鎵ц浜哄叧鑱斾俊鎭�
-        List<SysTaskAssignee> assignees = sysTaskAssigneeMapper.selectSysTaskAssigneeByTaskId(taskId);
-        SysTaskAssignee targetAssignee = assignees.stream()
-            .filter(a -> a.getUserId().equals(userId))
-            .findFirst()
-            .orElse(null);
-        
-        if (targetAssignee == null) {
-            return com.ruoyi.common.core.domain.AjaxResult.error("鎮ㄤ笉鏄浠诲姟鐨勬墽琛屼汉");
-        }
-        
-        // 鏇存柊灏辩华鐘舵��
-        targetAssignee.setIsReady("0");
-        targetAssignee.setReadyTime(null);
-        targetAssignee.setUpdateBy(SecurityUtils.getUsername());
-        targetAssignee.setUpdateTime(new Date());
-        
-        sysTaskAssigneeMapper.updateSysTaskAssignee(targetAssignee);
-        
-        return com.ruoyi.common.core.domain.AjaxResult.success("宸插彇娑堝氨缁�");
+    public AjaxResult cancelAssigneeReady(Long taskId, Long userId) {
+        return sysTaskAssigneeService.cancelAssigneeReady(taskId, userId);
     }
    
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskVehicleServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskVehicleServiceImpl.java
index feab794..6f3a6d7 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskVehicleServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskVehicleServiceImpl.java
@@ -1,55 +1,31 @@
 package com.ruoyi.system.service.impl;
 
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.system.domain.SysTaskVehicle;
+import com.ruoyi.system.domain.VehicleInfo;
+import com.ruoyi.system.mapper.SysTaskVehicleMapper;
+import com.ruoyi.system.mapper.VehicleInfoMapper;
+import com.ruoyi.system.service.ISysTaskVehicleService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-import com.ruoyi.common.utils.DateUtils;
-import com.ruoyi.common.utils.SecurityUtils;
-import com.ruoyi.system.mapper.SysTaskVehicleMapper;
-import com.ruoyi.system.mapper.SysTaskMapper;
-import com.ruoyi.system.mapper.VehicleInfoMapper;
-import com.ruoyi.system.domain.SysTask;
-import com.ruoyi.system.domain.SysTaskVehicle;
-import com.ruoyi.system.domain.VehicleInfo;
-import com.ruoyi.system.service.ISysTaskVehicleService;
+
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
- * 浠诲姟杞﹁締鍏宠仈Service涓氬姟灞傚鐞�
+ * 浠诲姟杞﹁締鏈嶅姟瀹炵幇绫�
  * 
  * @author ruoyi
- * @date 2024-01-15
  */
 @Service
 public class SysTaskVehicleServiceImpl implements ISysTaskVehicleService {
-    
-    private static final Logger logger = LoggerFactory.getLogger(SysTaskVehicleServiceImpl.class);
-    
+
     @Autowired
     private SysTaskVehicleMapper sysTaskVehicleMapper;
-    
+
     @Autowired
     private VehicleInfoMapper vehicleInfoMapper;
-    
-    @Autowired
-    private SysTaskMapper sysTaskMapper;
-
-    /**
-     * 鏌ヨ浠诲姟杞﹁締鍏宠仈
-     * 
-     * @param id 浠诲姟杞﹁締鍏宠仈涓婚敭
-     * @return 浠诲姟杞﹁締鍏宠仈
-     */
-    @Override
-    public SysTaskVehicle selectSysTaskVehicleById(Long id) {
-        return sysTaskVehicleMapper.selectSysTaskVehicleById(id);
-    }
 
     /**
      * 鏌ヨ浠诲姟杞﹁締鍏宠仈鍒楄〃
@@ -60,6 +36,17 @@
     @Override
     public List<SysTaskVehicle> selectSysTaskVehicleList(SysTaskVehicle sysTaskVehicle) {
         return sysTaskVehicleMapper.selectSysTaskVehicleList(sysTaskVehicle);
+    }
+
+    /**
+     * 鏌ヨ浠诲姟杞﹁締鍏宠仈淇℃伅
+     * 
+     * @param id 浠诲姟杞﹁締鍏宠仈涓婚敭
+     * @return 浠诲姟杞﹁締鍏宠仈淇℃伅
+     */
+    @Override
+    public SysTaskVehicle selectSysTaskVehicleById(Long id) {
+        return sysTaskVehicleMapper.selectSysTaskVehicleById(id);
     }
 
     /**
@@ -80,20 +67,7 @@
      * @return 缁撴灉
      */
     @Override
-    @Transactional
     public int insertSysTaskVehicle(SysTaskVehicle sysTaskVehicle) {
-        // 璁剧疆鍒嗛厤鏃堕棿鍜屽垎閰嶄汉
-        if (sysTaskVehicle.getAssignTime() == null) {
-            sysTaskVehicle.setAssignTime(DateUtils.getNowDate());
-        }
-        if (sysTaskVehicle.getAssignBy() == null || sysTaskVehicle.getAssignBy().isEmpty()) {
-            sysTaskVehicle.setAssignBy(SecurityUtils.getUsername());
-        }
-        // 璁剧疆榛樿鐘舵��
-        if (sysTaskVehicle.getStatus() == null || sysTaskVehicle.getStatus().isEmpty()) {
-            sysTaskVehicle.setStatus("ASSIGNED");
-        }
-        sysTaskVehicle.setCreateTime(DateUtils.getNowDate());
         return sysTaskVehicleMapper.insertSysTaskVehicle(sysTaskVehicle);
     }
 
@@ -104,22 +78,8 @@
      * @return 缁撴灉
      */
     @Override
-    @Transactional
     public int updateSysTaskVehicle(SysTaskVehicle sysTaskVehicle) {
-        sysTaskVehicle.setUpdateTime(DateUtils.getNowDate());
         return sysTaskVehicleMapper.updateSysTaskVehicle(sysTaskVehicle);
-    }
-
-    /**
-     * 鎵归噺鍒犻櫎浠诲姟杞﹁締鍏宠仈
-     * 
-     * @param ids 闇�瑕佸垹闄ょ殑浠诲姟杞﹁締鍏宠仈涓婚敭
-     * @return 缁撴灉
-     */
-    @Override
-    @Transactional
-    public int deleteSysTaskVehicleByIds(Long[] ids) {
-        return sysTaskVehicleMapper.deleteSysTaskVehicleByIds(ids);
     }
 
     /**
@@ -129,186 +89,30 @@
      * @return 缁撴灉
      */
     @Override
-    @Transactional
     public int deleteSysTaskVehicleById(Long id) {
         return sysTaskVehicleMapper.deleteSysTaskVehicleById(id);
     }
 
     /**
-     * 鏍规嵁浠诲姟ID鍒犻櫎杞﹁締鍏宠仈
+     * 鎵归噺鍒犻櫎浠诲姟杞﹁締鍏宠仈淇℃伅
      * 
-     * @param taskId 浠诲姟ID
+     * @param ids 闇�瑕佸垹闄ょ殑涓婚敭闆嗗悎
      * @return 缁撴灉
      */
     @Override
-    @Transactional
-    public int deleteSysTaskVehicleByTaskId(Long taskId) {
-        return sysTaskVehicleMapper.deleteSysTaskVehicleByTaskId(taskId);
-    }
-
-    /**
-     * 鏍规嵁浠诲姟ID鍜岃溅杈咺D鍒犻櫎鍏宠仈
-     * 
-     * @param taskId 浠诲姟ID
-     * @param vehicleId 杞﹁締ID
-     * @return 缁撴灉
-     */
-    @Override
-    @Transactional
-    public int deleteSysTaskVehicleByTaskIdAndVehicleId(Long taskId, Long vehicleId) {
-        return sysTaskVehicleMapper.deleteSysTaskVehicleByTaskIdAndVehicleId(taskId, vehicleId);
-    }
-
-    /**
-     * 妫�鏌ヤ换鍔¤溅杈嗗叧鑱旀槸鍚﹀瓨鍦�
-     * 
-     * @param taskId 浠诲姟ID
-     * @param vehicleId 杞﹁締ID
-     * @return 缁撴灉
-     */
-    @Override
-    public int checkTaskVehicleExists(Long taskId, Long vehicleId) {
-        return sysTaskVehicleMapper.checkTaskVehicleExists(taskId, vehicleId);
-    }
-
-    /**
-     * 鎵归噺鏂板浠诲姟杞﹁締鍏宠仈
-     * 
-     * @param sysTaskVehicleList 浠诲姟杞﹁締鍏宠仈鍒楄〃
-     * @return 缁撴灉
-     */
-    @Override
-    @Transactional
-    public int batchInsertSysTaskVehicle(List<SysTaskVehicle> sysTaskVehicleList) {
-        return sysTaskVehicleMapper.batchInsertSysTaskVehicle(sysTaskVehicleList);
-    }
-
-    /**
-     * 鍒嗛厤杞﹁締缁欎换鍔�
-     * 
-     * @param taskId 浠诲姟ID
-     * @param vehicleId 杞﹁締ID
-     * @param remark 澶囨敞
-     * @return 缁撴灉
-     */
-    @Override
-    @Transactional
-    public int assignVehicleToTask(Long taskId, Long vehicleId, String remark) {
-        // 妫�鏌ユ槸鍚﹀凡缁忓垎閰�
-        int exists = sysTaskVehicleMapper.checkTaskVehicleExists(taskId, vehicleId);
-        if (exists > 0) {
-            throw new RuntimeException("杞﹁締宸茬粡鍒嗛厤缁欒浠诲姟");
-        }
-        
-        SysTaskVehicle taskVehicle = new SysTaskVehicle();
-        taskVehicle.setTaskId(taskId);
-        taskVehicle.setVehicleId(vehicleId);
-        taskVehicle.setAssignTime(DateUtils.getNowDate());
-        taskVehicle.setAssignBy(SecurityUtils.getUsername());
-        taskVehicle.setStatus("ASSIGNED");
-        taskVehicle.setRemark(remark);
-        
-        return sysTaskVehicleMapper.insertSysTaskVehicle(taskVehicle);
-    }
-
-    /**
-     * 鍙栨秷浠诲姟杞﹁締鍒嗛厤
-     * 
-     * @param taskId 浠诲姟ID
-     * @param vehicleId 杞﹁締ID
-     * @return 缁撴灉
-     */
-    @Override
-    @Transactional
-    public int unassignVehicleFromTask(Long taskId, Long vehicleId) {
-        return sysTaskVehicleMapper.deleteSysTaskVehicleByTaskIdAndVehicleId(taskId, vehicleId);
-    }
-
-    /**
-     * 鎵归噺鍒嗛厤杞﹁締缁欎换鍔�
-     * 
-     * @param taskId 浠诲姟ID
-     * @param vehicleIds 杞﹁締ID鍒楄〃
-     * @param remark 澶囨敞
-     * @return 缁撴灉
-     */
-    @Override
-    @Transactional
-    public int assignMultipleVehiclesToTask(Long taskId, List<Long> vehicleIds, String remark) {
-        List<SysTaskVehicle> taskVehicles = new ArrayList<>();
-        Date now = DateUtils.getNowDate();
-        String assignBy = SecurityUtils.getUsername();
-        
-        for (Long vehicleId : vehicleIds) {
-            // 妫�鏌ユ槸鍚﹀凡缁忓垎閰�
-            int exists = sysTaskVehicleMapper.checkTaskVehicleExists(taskId, vehicleId);
-            if (exists == 0) {
-                SysTaskVehicle taskVehicle = new SysTaskVehicle();
-                taskVehicle.setTaskId(taskId);
-                taskVehicle.setVehicleId(vehicleId);
-                taskVehicle.setAssignTime(now);
-                taskVehicle.setAssignBy(assignBy);
-                taskVehicle.setStatus("ASSIGNED");
-                taskVehicle.setRemark(remark);
-                taskVehicles.add(taskVehicle);
-            }
-        }
-        
-        if (!taskVehicles.isEmpty()) {
-            return sysTaskVehicleMapper.batchInsertSysTaskVehicle(taskVehicles);
-        }
-        
-        return 0;
-    }
-
-    /**
-     * 鏌ヨ鍙敤杞﹁締
-     * 
-     * @param deptId 閮ㄩ棬ID
-     * @param taskType 浠诲姟绫诲瀷
-     * @return 鍙敤杞﹁締鍒楄〃
-     */
-    @Override
-    public List<SysTaskVehicle> getAvailableVehicles(Long deptId, String taskType) {
-        // 鏌ヨ鎸囧畾閮ㄩ棬涓嬬姸鎬佷负姝e父鐨勮溅杈�
-        List<SysTaskVehicle> availableVehicles = new ArrayList<>();
-        
-        try {
-            // 鏌ヨ杞﹁締淇℃伅
-            VehicleInfo queryParam = new VehicleInfo();
-            queryParam.setDeptId(deptId);
-            queryParam.setStatus("0"); // 0琛ㄧず姝e父鐘舵��
-            
-            List<VehicleInfo> vehicles = vehicleInfoMapper.selectVehicleInfoList(queryParam);
-            
-            // 杞崲涓� SysTaskVehicle 瀵硅薄
-            for (VehicleInfo vehicle : vehicles) {
-                SysTaskVehicle taskVehicle = new SysTaskVehicle();
-                taskVehicle.setVehicleId(vehicle.getVehicleId());
-                taskVehicle.setVehicleNo(vehicle.getVehicleNo());
-                taskVehicle.setVehicleType(vehicle.getVehicleType());
-                taskVehicle.setVehicleBrand(vehicle.getVehicleBrand());
-                taskVehicle.setVehicleModel(vehicle.getVehicleModel());
-                taskVehicle.setStatus(vehicle.getStatus());
-                availableVehicles.add(taskVehicle);
-            }
-        } catch (Exception e) {
-            logger.error("鏌ヨ鍙敤杞﹁締澶辫触", e);
-        }
-        
-        return availableVehicles;
+    public int deleteSysTaskVehicleByIds(Long[] ids) {
+        return sysTaskVehicleMapper.deleteSysTaskVehicleByIds(ids);
     }
 
     /**
      * 鏇存柊浠诲姟杞﹁締鍏宠仈鐘舵��
      * 
-     * @param id 鍏宠仈ID
-     * @param status 鏂扮姸鎬�
+     * @param id 浠诲姟杞﹁締鍏宠仈涓婚敭
+     * @param status 鐘舵��
      * @return 缁撴灉
      */
     @Override
-    @Transactional
-    public int updateTaskVehicleStatus(Long id, String status) {
+    public int updateSysTaskVehicleStatus(Long id, String status) {
         SysTaskVehicle taskVehicle = new SysTaskVehicle();
         taskVehicle.setId(id);
         taskVehicle.setStatus(status);
@@ -320,58 +124,195 @@
      * 鎵归噺鑾峰彇杞﹁締褰撳墠浠诲姟鐘舵��
      * 
      * @param vehicleIds 杞﹁締ID鍒楄〃
-     * @return Map<杞﹁締ID, Map<"taskCode": 浠诲姟缂栧彿, "taskStatus": 浠诲姟鐘舵��>>
+     * @return 杞﹁締浠诲姟鐘舵�佹槧灏�
      */
     @Override
     public Map<Long, Map<String, Object>> batchGetVehicleCurrentTaskStatus(List<Long> vehicleIds) {
-        Map<Long, Map<String, Object>> resultMap = new HashMap<>();
+        Map<Long, Map<String, Object>> statusMap = new HashMap<>();
         
-        if (vehicleIds == null || vehicleIds.isEmpty()) {
-            return resultMap;
-        }
-        
-        try {
-            // 瀵规瘡涓溅杈嗘煡璇㈠叾褰撳墠姝e湪杩涜鐨勪换鍔�
+        if (vehicleIds != null && !vehicleIds.isEmpty()) {
+            // 杩欓噷鍙互鏍规嵁瀹為檯闇�姹傛煡璇㈣溅杈嗙殑褰撳墠浠诲姟鐘舵��
+            // 鐢变簬缂轰箯鍏蜂綋鐨勪笟鍔¢�昏緫锛岃繖閲岀畝鍗曡繑鍥炵┖鏄犲皠
             for (Long vehicleId : vehicleIds) {
-                if (vehicleId == null) {
-                    continue;
-                }
-                
-                // 鏌ヨ杞﹁締鐨勬椿璺冧换鍔★紙鏈畬鎴愩�佹湭鍙栨秷鐨勪换鍔★級
-                List<SysTask> activeTasks = sysTaskMapper.selectActiveTasksByVehicleId(vehicleId);
-                
-                if (activeTasks != null && !activeTasks.isEmpty()) {
-                    // 鍙栫涓�涓椿璺冧换鍔★紙鏈�鏂扮殑锛�
-                    SysTask currentTask = activeTasks.get(0);
-                    
-                    Map<String, Object> taskInfo = new HashMap<>();
-                    taskInfo.put("taskCode", currentTask.getTaskCode());
-                    taskInfo.put("taskStatus", currentTask.getTaskStatus());
-                    taskInfo.put("taskId", currentTask.getTaskId());
-                    
-                    resultMap.put(vehicleId, taskInfo);
-                } else {
-                    // 娌℃湁娲昏穬浠诲姟
-                    resultMap.put(vehicleId, null);
-                }
+                Map<String, Object> statusInfo = new HashMap<>();
+                statusInfo.put("vehicleId", vehicleId);
+                statusInfo.put("currentTaskId", null);
+                statusInfo.put("currentTaskStatus", "AVAILABLE");
+                statusInfo.put("lastUpdateTime", DateUtils.getNowDate());
+                statusMap.put(vehicleId, statusInfo);
             }
-        } catch (Exception e) {
-            logger.error("鎵归噺鏌ヨ杞﹁締浠诲姟鐘舵�佸け璐�", e);
         }
         
-        return resultMap;
+        return statusMap;
     }
 
-    /**
-     * 鏇存柊浠诲姟杞﹁締鍏宠仈鐘舵�侊紙鏂版柟娉曪級
-     * 
-     * @param id 鍏宠仈ID
-     * @param status 鏂扮姸鎬�
-     * @return 缁撴灉
-     */
     @Override
     @Transactional
-    public int updateSysTaskVehicleStatus(Long id, String status) {
-        return updateTaskVehicleStatus(id, status);
+    public void saveTaskVehicles(Long taskId, List<Long> vehicleIds, String userName) {
+        saveTaskVehicles(taskId, vehicleIds, userName, 
+            DateUtils.getNowDate(), DateUtils.getNowDate(), DateUtils.getNowDate());
+    }
+
+    @Override
+    @Transactional
+    public void saveTaskVehicles(Long taskId, List<Long> vehicleIds, String userName,
+                                  Date assignTime, Date createTime, Date updateTime) {
+        if (vehicleIds == null || vehicleIds.isEmpty()) {
+            return;
+        }
+        
+        for (Long vehicleId : vehicleIds) {
+            SysTaskVehicle taskVehicle = new SysTaskVehicle();
+            taskVehicle.setTaskId(taskId);
+            taskVehicle.setVehicleId(vehicleId);
+            taskVehicle.setAssignTime(assignTime);
+            taskVehicle.setAssignBy(userName);
+            taskVehicle.setStatus("ASSIGNED");
+            taskVehicle.setCreateBy(userName);
+            taskVehicle.setCreateTime(createTime);
+            taskVehicle.setUpdateBy(userName);
+            taskVehicle.setUpdateTime(updateTime);
+            
+            sysTaskVehicleMapper.insertSysTaskVehicle(taskVehicle);
+        }
+    }
+
+    @Override
+    @Transactional
+    public boolean updateTaskVehicles(Long taskId, List<Long> newVehicleIds, String userName) {
+        if (newVehicleIds == null || newVehicleIds.isEmpty()) {
+            return false;
+        }
+        
+        // 鏌ヨ鐜版湁鐨勮溅杈嗗叧鑱�
+        List<SysTaskVehicle> existingVehicles = sysTaskVehicleMapper.selectSysTaskVehicleByTaskId(taskId);
+        List<Long> existingVehicleIds = existingVehicles.stream()
+            .map(SysTaskVehicle::getVehicleId)
+            .collect(Collectors.toList());
+        
+        // 姣旇緝鏂版棫杞﹁締ID鍒楄〃锛屽垽鏂槸鍚︽湁鍙樺寲
+        boolean vehiclesChanged = !new HashSet<>(existingVehicleIds).equals(new HashSet<>(newVehicleIds));
+        
+        // 鍙湁杞﹁締鍙戠敓鍙樺寲鏃舵墠鏇存柊
+        if (vehiclesChanged) {
+            // 鍒犻櫎鏃х殑杞﹁締鍏宠仈
+            sysTaskVehicleMapper.deleteSysTaskVehicleByTaskId(taskId);
+            
+            // 娣诲姞鏂扮殑杞﹁締鍏宠仈
+            Date now = DateUtils.getNowDate();
+            for (Long vehicleId : newVehicleIds) {
+                SysTaskVehicle taskVehicle = new SysTaskVehicle();
+                taskVehicle.setTaskId(taskId);
+                taskVehicle.setVehicleId(vehicleId);
+                taskVehicle.setAssignTime(now);
+                taskVehicle.setAssignBy(userName);
+                taskVehicle.setCreateTime(now);
+                sysTaskVehicleMapper.insertSysTaskVehicle(taskVehicle);
+            }
+            return true;
+        }
+        
+        return false;
+    }
+
+    @Override
+    @Transactional
+    public int assignVehicleToTask(Long taskId, Long vehicleId, String remark, Long userId, String userName) {
+        // 妫�鏌ユ槸鍚﹀凡缁忓垎閰�
+        int exists = sysTaskVehicleMapper.checkTaskVehicleExists(taskId, vehicleId);
+        if (exists > 0) {
+            throw new RuntimeException("杞﹁締宸茬粡鍒嗛厤缁欒浠诲姟");
+        }
+        
+        SysTaskVehicle taskVehicle = new SysTaskVehicle();
+        taskVehicle.setTaskId(taskId);
+        taskVehicle.setVehicleId(vehicleId);
+        taskVehicle.setAssignTime(DateUtils.getNowDate());
+        taskVehicle.setAssignBy(userName);
+        taskVehicle.setStatus("ASSIGNED");
+        taskVehicle.setRemark(remark);
+        
+        return sysTaskVehicleMapper.insertSysTaskVehicle(taskVehicle);
+    }
+
+    @Override
+    @Transactional
+    public int unassignVehicleFromTask(Long taskId, Long vehicleId) {
+        return sysTaskVehicleMapper.deleteSysTaskVehicleByTaskIdAndVehicleId(taskId, vehicleId);
+    }
+
+    @Override
+    @Transactional
+    public int assignMultipleVehiclesToTask(Long taskId, List<Long> vehicleIds, String remark, 
+                                            Long userId, String userName) {
+        List<SysTaskVehicle> taskVehicles = new ArrayList<>();
+        Date now = DateUtils.getNowDate();
+        
+        for (Long vehicleId : vehicleIds) {
+            // 妫�鏌ユ槸鍚﹀凡缁忓垎閰�
+            int exists = sysTaskVehicleMapper.checkTaskVehicleExists(taskId, vehicleId);
+            if (exists == 0) {
+                SysTaskVehicle taskVehicle = new SysTaskVehicle();
+                taskVehicle.setTaskId(taskId);
+                taskVehicle.setVehicleId(vehicleId);
+                taskVehicle.setAssignTime(now);
+                taskVehicle.setAssignBy(userName);
+                taskVehicle.setStatus("ASSIGNED");
+                taskVehicle.setRemark(remark);
+                taskVehicles.add(taskVehicle);
+            }
+        }
+        
+        int result = 0;
+        if (!taskVehicles.isEmpty()) {
+            result = sysTaskVehicleMapper.batchInsertSysTaskVehicle(taskVehicles);
+        }
+        
+        return result;
+    }
+
+    @Override
+    public List<SysTaskVehicle> getTaskVehicles(Long taskId) {
+        return sysTaskVehicleMapper.selectSysTaskVehicleByTaskId(taskId);
+    }
+
+    @Override
+    public List<SysTaskVehicle> getAvailableVehicles(Long deptId, String taskType) {
+        List<SysTaskVehicle> availableVehicles = new ArrayList<>();
+        
+        try {
+            // 鏌ヨ鎵�鏈夌姸鎬佷负姝e父鐨勮溅杈�
+            VehicleInfo queryParam = new VehicleInfo();
+            queryParam.setStatus("0"); // 0琛ㄧず姝e父鐘舵��
+            queryParam.setDeptId(deptId);
+            
+            List<VehicleInfo> vehicles = vehicleInfoMapper.selectVehicleInfoList(queryParam);
+            
+            // 杞崲涓篠ysTaskVehicle瀵硅薄
+            for (VehicleInfo vehicle : vehicles) {
+                SysTaskVehicle taskVehicle = new SysTaskVehicle();
+                taskVehicle.setVehicleId(vehicle.getVehicleId());
+                taskVehicle.setVehicleNo(vehicle.getVehicleNo());
+                taskVehicle.setVehicleType(vehicle.getVehicleType());
+                taskVehicle.setVehicleBrand(vehicle.getVehicleBrand());
+                taskVehicle.setVehicleModel(vehicle.getVehicleModel());
+                taskVehicle.setDeptName(vehicle.getDeptName());
+                availableVehicles.add(taskVehicle);
+            }
+        } catch (Exception e) {
+            System.err.println("鏌ヨ鍙敤杞﹁締澶辫触: " + e.getMessage());
+        }
+        
+        return availableVehicles;
+    }
+
+    @Override
+    public int deleteTaskVehiclesByTaskId(Long taskId) {
+        return sysTaskVehicleMapper.deleteSysTaskVehicleByTaskId(taskId);
+    }
+
+    @Override
+    public boolean checkVehicleAssigned(Long taskId, Long vehicleId) {
+        return sysTaskVehicleMapper.checkTaskVehicleExists(taskId, vehicleId) > 0;
     }
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysWelfareTaskServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysWelfareTaskServiceImpl.java
new file mode 100644
index 0000000..954e0db
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysWelfareTaskServiceImpl.java
@@ -0,0 +1,131 @@
+package com.ruoyi.system.service.impl;
+
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.system.domain.SysTaskWelfare;
+import com.ruoyi.system.domain.vo.TaskCreateVO;
+import com.ruoyi.system.mapper.SysTaskWelfareMapper;
+import com.ruoyi.system.service.ISysWelfareTaskService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+
+/**
+ * 绂忕杞︿换鍔℃湇鍔″疄鐜扮被
+ * 
+ * @author ruoyi
+ */
+@Service
+public class SysWelfareTaskServiceImpl implements ISysWelfareTaskService {
+
+    @Autowired
+    private SysTaskWelfareMapper sysTaskWelfareMapper;
+
+    @Override
+    public void saveWelfareInfo(Long taskId, String userName, TaskCreateVO createVO) {
+        SysTaskWelfare welfareInfo = new SysTaskWelfare();
+        welfareInfo.setTaskId(taskId);
+        
+        // 璁剧疆涔樺淇℃伅
+        if (createVO.getPassenger() != null) {
+            welfareInfo.setPassengerContact(createVO.getPassenger().getContact());
+            welfareInfo.setPassengerPhone(createVO.getPassenger().getPhone());
+        }
+        
+        // 璁剧疆鍦板潃淇℃伅
+        welfareInfo.setPickupAddress(createVO.getStartAddress());
+        welfareInfo.setDestinationAddress(createVO.getEndAddress());
+        
+        // 璁剧疆GPS鍧愭爣
+        welfareInfo.setPickupLongitude(createVO.getDepartureLongitude());
+        welfareInfo.setPickupLatitude(createVO.getDepartureLatitude());
+        welfareInfo.setDestinationLongitude(createVO.getDestinationLongitude());
+        welfareInfo.setDestinationLatitude(createVO.getDestinationLatitude());
+        
+        // 璁剧疆璺濈鍜岃垂鐢�
+        // 浼樺厛浣跨敤绂忕杞︿笓鐢ㄧ殑distance瀛楁锛屽鏋滄病鏈夊垯浣跨敤閫氱敤鐨別stimatedDistance
+        BigDecimal serviceDistance = createVO.getDistance() != null ? createVO.getDistance() : createVO.getEstimatedDistance();
+        welfareInfo.setServiceDistance(serviceDistance);
+        welfareInfo.setServicePrice(createVO.getPrice());
+        
+        // 绯荤粺瀛楁
+        welfareInfo.setCreateTime(DateUtils.getNowDate());
+        welfareInfo.setUpdateTime(DateUtils.getNowDate());
+        welfareInfo.setCreateBy(userName);
+        welfareInfo.setUpdateBy(userName);
+        
+        sysTaskWelfareMapper.insertSysTaskWelfare(welfareInfo);
+    }
+
+    @Override
+    public void updateWelfareInfo(Long taskId, TaskCreateVO createVO, String userName) {
+        // 鏌ヨ鐜版湁鐨勬墿灞曚俊鎭�
+        SysTaskWelfare existingInfo = sysTaskWelfareMapper.selectSysTaskWelfareByTaskId(taskId);
+        if (existingInfo == null) {
+            // 濡傛灉涓嶅瓨鍦紝鍒欏垱寤烘柊鐨�
+            existingInfo = new SysTaskWelfare();
+            existingInfo.setTaskId(taskId);
+            existingInfo.setCreateTime(DateUtils.getNowDate());
+            existingInfo.setCreateBy(userName);
+        }
+        
+        // 鏇存柊涔樺淇℃伅
+        if (createVO.getPassenger() != null) {
+            if (createVO.getPassenger().getContact() != null) {
+                existingInfo.setPassengerContact(createVO.getPassenger().getContact());
+            }
+            if (createVO.getPassenger().getPhone() != null) {
+                existingInfo.setPassengerPhone(createVO.getPassenger().getPhone());
+            }
+        }
+        
+        // 鏇存柊鍦板潃淇℃伅
+        if (createVO.getStartAddress() != null) {
+            existingInfo.setPickupAddress(createVO.getStartAddress());
+        }
+        if (createVO.getEndAddress() != null) {
+            existingInfo.setDestinationAddress(createVO.getEndAddress());
+        }
+        
+        // 鏇存柊GPS鍧愭爣
+        if (createVO.getDepartureLongitude() != null) {
+            existingInfo.setPickupLongitude(createVO.getDepartureLongitude());
+        }
+        if (createVO.getDepartureLatitude() != null) {
+            existingInfo.setPickupLatitude(createVO.getDepartureLatitude());
+        }
+        if (createVO.getDestinationLongitude() != null) {
+            existingInfo.setDestinationLongitude(createVO.getDestinationLongitude());
+        }
+        if (createVO.getDestinationLatitude() != null) {
+            existingInfo.setDestinationLatitude(createVO.getDestinationLatitude());
+        }
+        
+        // 鏇存柊璺濈鍜岃垂鐢�
+        if (createVO.getDistance() != null) {
+            existingInfo.setServiceDistance(createVO.getDistance());
+        } else if (createVO.getEstimatedDistance() != null) {
+            existingInfo.setServiceDistance(createVO.getEstimatedDistance());
+        }
+        if (createVO.getPrice() != null) {
+            existingInfo.setServicePrice(createVO.getPrice());
+        }
+        
+        // 绯荤粺瀛楁
+        existingInfo.setUpdateTime(DateUtils.getNowDate());
+        existingInfo.setUpdateBy(userName);
+        
+        // 鎵ц鏇存柊
+        sysTaskWelfareMapper.updateSysTaskWelfare(existingInfo);
+    }
+
+    @Override
+    public SysTaskWelfare getWelfareInfoByTaskId(Long taskId) {
+        return sysTaskWelfareMapper.selectSysTaskWelfareByTaskId(taskId);
+    }
+
+    @Override
+    public int deleteWelfareInfoByTaskId(Long taskId) {
+        return sysTaskWelfareMapper.deleteSysTaskWelfareByTaskId(taskId);
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TiandituMapServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TiandituMapServiceImpl.java
index a5c7675..57cfe6c 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TiandituMapServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TiandituMapServiceImpl.java
@@ -41,7 +41,14 @@
         if (address == null || address.trim().isEmpty()) {
             return null;
         }
-        
+        if(address.indexOf(",")>0){
+            String[] split = address.split(",");
+            address=split[0];
+        }
+        if(address.indexOf("锛�")>0){
+            String[] split = address.split("锛�");
+            address=split[0];
+        }
         try {
             // 鏋勫缓澶╁湴鍥惧湴鐞嗙紪鐮丄PI URL
             String url = "http://api.tianditu.gov.cn/geocoder";
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 fbeede2..3738cfb 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
@@ -96,10 +96,19 @@
     @Override
     public int batchCalculateSegmentMileage(Date startTime, Date endTime) {
         try {
-            logger.info("寮�濮嬫壒閲忚绠桮PS鍒嗘閲岀▼ - 鏃堕棿鑼冨洿: {} 鍒� {}", startTime, endTime);
+//            logger.info("寮�濮嬫壒閲忚绠桮PS鍒嗘閲岀▼ - 鏃堕棿鑼冨洿: {} 鍒� {}", startTime, endTime);
             
-            // 鏌ヨ鍦ㄦ寚瀹氭椂闂磋寖鍥村唴鏈塆PS鏁版嵁鐨勬墍鏈夎溅杈�
+            // 鏌ヨ鍦ㄦ寚瀹氭椂闂磋寖鍥村唴鏈塆PS鏁版嵁鐨勬墍鏈夎溅杈嗭紙娣诲姞鎱QL鐩戞帶锛�
+            long startQueryTime = System.currentTimeMillis();
             List<Long> vehicleIds = vehicleGpsMapper.selectActiveVehicleIds(startTime);
+            long queryTime = System.currentTimeMillis() - startQueryTime;
+            
+            // 鎱㈡煡璇㈣鍛婏紙瓒呰繃1绉掞級
+            if (queryTime > 1000) {
+                logger.warn("鏌ヨ娲昏穬杞﹁締ID鑰楁椂杩囬暱: {}ms, 寮�濮嬫椂闂�: {}, 寤鸿妫�鏌� tb_vehicle_gps 琛ㄧ殑绱㈠紩锛堥渶瑕� vehicle_id 鍜� collect_time 缁勫悎绱㈠紩锛�", 
+                    queryTime, startTime);
+            }
+            logger.info("鏌ヨ鍒� {} 杈嗘椿璺冭溅杈嗭紝鏌ヨ鑰楁椂: {}ms", vehicleIds != null ? vehicleIds.size() : 0, queryTime);
             
             if (vehicleIds == null || vehicleIds.isEmpty()) {
                 logger.info("娌℃湁鎵惧埌娲昏穬杞﹁締");
@@ -115,14 +124,14 @@
             for (int i = 0; i < vehicleIds.size(); i++) {
                 Long vehicleId = vehicleIds.get(i);
                 try {
-                    logger.info("姝e湪澶勭悊杞﹁締 {} ({}/{})", vehicleId, i + 1, vehicleIds.size());
+//                    logger.info("姝e湪澶勭悊杞﹁締 {} ({}/{})", vehicleId, i + 1, vehicleIds.size());
                     
                     int segmentCount = calculateVehicleSegmentMileage(vehicleId, startTime, endTime);
                     if (segmentCount > 0) {
                         successCount++;
-                        logger.info("杞﹁締 {} 璁$畻鎴愬姛锛岀敓鎴� {} 涓垎娈佃褰�", vehicleId, segmentCount);
+//                        logger.info("杞﹁締 {} 璁$畻鎴愬姛锛岀敓鎴� {} 涓垎娈佃褰�", vehicleId, segmentCount);
                     } else {
-                        logger.debug("杞﹁締 {} 鏃犳湁鏂扮殑GPS鍒嗘鏁版嵁", vehicleId);
+//                        logger.debug("杞﹁締 {} 鏃犳湁鏂扮殑GPS鍒嗘鏁版嵁", vehicleId);
                     }
                 } catch (Exception e) {
                     failedCount++;
@@ -133,12 +142,12 @@
                 
                 // 姣忓鐞�10杈嗚溅杈撳嚭涓�娆¤繘搴�
                 if ((i + 1) % 10 == 0) {
-                    logger.info("鎵归噺璁$畻杩涘害: {}/{}, 鎴愬姛: {}, 澶辫触: {}", 
+                    logger.info("鎵归噺璁$畻杩涘害: {}/{}, 鎴愬姛: {}, 澶辫触: {}",
                                i + 1, vehicleIds.size(), successCount, failedCount);
                 }
             }
             
-            logger.info("鎵归噺鍒嗘閲岀▼璁$畻瀹屾垚 - 鏃堕棿鑼冨洿: {} 鍒� {}, 鎬昏溅杈嗘暟: {}, 鎴愬姛: {}, 澶辫触: {}", 
+            logger.info("鎵归噺鍒嗘閲岀▼璁$畻瀹屾垚 - 鏃堕棿鑼冨洿: {} 鍒� {}, 鎬昏溅杈嗘暟: {}, 鎴愬姛: {}, 澶辫触: {}",
                        startTime, endTime, vehicleIds.size(), successCount, failedCount);
             return successCount;
             
@@ -831,7 +840,7 @@
                                       Date segmentStartTime, Date segmentEndTime) {
         try {
             // 鏌ヨ璇ヨ溅杈嗘鍦ㄦ墽琛岀殑浠诲姟鍒楄〃
-            List<SysTask> activeTasks = sysTaskMapper.selectActiveTasksByVehicleId(vehicleId);
+            List<SysTask> activeTasks = sysTaskMapper.selectTaskByVehicleIdAndDate(vehicleId,segmentStartTime,segmentEndTime);
             
             if (activeTasks == null || activeTasks.isEmpty()) {
                 logger.debug("杞﹁締 {} 鍦ㄦ椂闂存 {} - {} 娌℃湁姝e湪鎵ц鐨勪换鍔�", vehicleId, segmentStartTime, segmentEndTime);
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleMileageStatsServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleMileageStatsServiceImpl.java
index f795378..2f59384 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleMileageStatsServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleMileageStatsServiceImpl.java
@@ -9,6 +9,8 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -117,16 +119,23 @@
             List<VehicleGps> gpsList = vehicleGpsMapper.selectGpsDataByTimeRange(vehicleId, dayStart, dayEnd);
             
             if (gpsList == null || gpsList.isEmpty()) {
-                logger.info("杞﹁締ID: {} 鍦ㄦ棩鏈�: {} 鏃燝PS鏁版嵁", vehicleId, statDate);
+//                logger.info("杞﹁締ID: {} 鍦ㄦ棩鏈�: {} 鏃燝PS鏁版嵁", vehicleId, statDate);
                 return null;
             }
-            
+//            logger.info("杞﹁締ID:{} GPS鏁版嵁鏉℃暟:{}", vehicleId, gpsList.size());
             // 3. 鏌ヨ杞﹁締鍦ㄨ鏃ユ湡鐨勪换鍔℃椂闂村尯闂�
             List<TaskTimeInterval> taskIntervals = vehicleMileageStatsMapper.selectTaskTimeIntervals(vehicleId, dayStart, dayEnd);
-            
-            // 4. 璁$畻閲岀▼
-            MileageCalculation calculation = calculateMileage(gpsList, taskIntervals);
-            
+
+
+           List<VehicleGpsSegmentMileage> mileages = this.getTaskDistanceMileage(vehicleId, dayStart, dayEnd);
+           int totalGpsPoints = mileages.stream()
+                .filter(segment -> segment.getGpsPointCount() != null)
+                .mapToInt(VehicleGpsSegmentMileage::getGpsPointCount)
+                .sum();
+            BigDecimal taskDistance = getTaskDistance(taskIntervals,mileages);
+
+            MileageCalculation calculation = calculateMileage(gpsList, taskDistance);
+//            logger.info("璁$畻鍑鸿溅杈嗗綋澶╂�婚噷绋嬶紝杞﹁締ID:{},鎬婚噷绋嬶細{},浠诲姟閲岀▼:{}",vehicleId,calculation.totalMileage,calculation.taskMileage);
             // 5. 鏌ヨ鎴栧垱寤虹粺璁¤褰�
             VehicleMileageStats stats = vehicleMileageStatsMapper.selectByVehicleIdAndDate(vehicleId, statDate);
             boolean isNew = (stats == null);
@@ -157,6 +166,9 @@
             stats.setTaskRatio(calculation.taskRatio);
             stats.setGpsPointCount(gpsList.size());
             stats.setTaskCount(taskIntervals == null ? 0 : taskIntervals.size());
+
+//            logger.info("杞﹁締ID: {} 鏃ユ湡: {} 閲岀▼缁熻瀹屾垚 - 鎬婚噷绋�: {}km, 浠诲姟閲岀▼: {}km, 闈炰换鍔¢噷绋�: {}km, 鍗犳瘮: {}",
+//                    vehicleId, statDate, calculation.totalMileage, taskDistance, calculation.nonTaskMileage, calculation.taskRatio);
             
             // 7. 淇濆瓨鍒版暟鎹簱
             if (isNew) {
@@ -216,10 +228,18 @@
         }
     }
 
+
+
+    private BigDecimal calculateTotalMileage(List<VehicleGpsSegmentMileage> mileages) {
+        return mileages.stream()
+            .map(mileage -> mileage.getSegmentDistance())
+            .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+    }
     /**
      * 璁$畻閲岀▼鐨勫唴閮ㄦ柟娉�
      */
-    private MileageCalculation calculateMileage(List<VehicleGps> gpsList, List<TaskTimeInterval> taskIntervals) {
+    private MileageCalculation calculateMileage(List<VehicleGps> gpsList, BigDecimal taskDistance) {
         MileageCalculation result = new MileageCalculation();
         
         // 閬嶅巻GPS鐐癸紝璁$畻鐩搁偦鐐逛箣闂寸殑璺濈
@@ -234,23 +254,12 @@
                 p2.getLatitude().doubleValue(), 
                 p2.getLongitude().doubleValue()
             );
-            
-            // 鑾峰彇杩欐璺濈鐨勬椂闂村尯闂�
-            Date segmentStart = parseDateTime(p1.getCollectTime());
-            Date segmentEnd = parseDateTime(p2.getCollectTime());
-            
-            // 璁$畻杩欐璺濈鍦ㄤ换鍔℃椂娈电殑鍗犳瘮
-            double taskRatio = calculateTaskOverlapRatio(segmentStart, segmentEnd, taskIntervals);
-            
-            // 鍒嗘憡閲岀▼
-            double taskDistance = distance * taskRatio;
-            double nonTaskDistance = distance * (1 - taskRatio);
-            
+
             result.totalMileage = result.totalMileage.add(BigDecimal.valueOf(distance));
-            result.taskMileage = result.taskMileage.add(BigDecimal.valueOf(taskDistance));
-            result.nonTaskMileage = result.nonTaskMileage.add(BigDecimal.valueOf(nonTaskDistance));
         }
-        
+
+        result.taskMileage=taskDistance;
+        result.nonTaskMileage=result.totalMileage.subtract(result.taskMileage);
         // 璁$畻浠诲姟閲岀▼鍗犳瘮
         if (result.totalMileage.compareTo(BigDecimal.ZERO) > 0) {
             result.taskRatio = result.taskMileage.divide(result.totalMileage, 4, RoundingMode.HALF_UP);
@@ -299,32 +308,92 @@
         return EARTH_RADIUS_KM * c;
     }
 
+    //璁$畻浠诲姟鏃堕棿娈靛唴鐨勯噷绋嬶紝搴旇鎷垮埌璇ヤ换鍔″湪宸ヤ綔鏃堕棿娈甸噷鐨勫垎娈佃窛绂荤劧鍚庣浉鍔�
+    private List<VehicleGpsSegmentMileage> getTaskDistanceMileage(Long vehicleId, Date segmentStart, Date segmentEnd) {
+        return segmentMileageMapper.selectSegmentsByDateRange(vehicleId, segmentStart, segmentEnd);
+    }
+
     /**
-     * 璁$畻鏃堕棿娈典笌浠诲姟鏃舵鐨勯噸鍙犳瘮渚�
+     * 璁$畻鍦ㄤ换鍔℃椂闂存鍐呯殑瀹為檯浠诲姟閲岀▼
+     * 閫氳繃妫�鏌ュ垎娈甸噷绋嬫暟鎹槸鍚︿笌浠诲姟鏃堕棿娈甸噸鍙狅紝绱姞杩欎簺閲嶅彔鍒嗘鐨勫疄闄呴噷绋�
+     * 
+     * @param taskTimeIntervals 浠诲姟鏃堕棿娈靛垪琛�
+     * @param segmentMileages 鍒嗘閲岀▼鏁版嵁鍒楄〃
+     * @return 鍦ㄤ换鍔℃椂闂存鍐呯殑鎬婚噷绋�
      */
-    private double calculateTaskOverlapRatio(Date segmentStart, Date segmentEnd, List<TaskTimeInterval> taskIntervals) {
-        if (taskIntervals == null || taskIntervals.isEmpty()) {
-            return 0.0;
+    private BigDecimal getTaskDistance(List<TaskTimeInterval> taskTimeIntervals, List<VehicleGpsSegmentMileage> segmentMileages) {
+        if (taskTimeIntervals == null || taskTimeIntervals.isEmpty() || 
+            segmentMileages == null || segmentMileages.isEmpty()) {
+            return BigDecimal.ZERO;
         }
         
-        long segmentDuration = segmentEnd.getTime() - segmentStart.getTime();
-        if (segmentDuration <= 0) {
-            return 0.0;
-        }
+        BigDecimal totalTaskDistance = BigDecimal.ZERO;
         
-        long totalOverlap = 0;
-        
-        for (TaskTimeInterval task : taskIntervals) {
-            // 璁$畻閲嶅彔鏃堕棿
-            long overlapStart = Math.max(segmentStart.getTime(), task.getStartTime().getTime());
-            long overlapEnd = Math.min(segmentEnd.getTime(), task.getEndTime().getTime());
+        // 閬嶅巻鎵�鏈夊垎娈甸噷绋嬫暟鎹�
+        for (VehicleGpsSegmentMileage segment : segmentMileages) {
+            // 鍙鐞嗘湁鍏宠仈浠诲姟ID涓旀湁璺濈鏁版嵁鐨勫垎娈�
+
+
+            // 妫�鏌ヨ鍒嗘鏄惁涓庝换浣曚换鍔℃椂闂存閲嶅彔
+            Date segmentStart = segment.getSegmentStartTime();
+            Date segmentEnd = segment.getSegmentEndTime();
             
-            if (overlapEnd > overlapStart) {
-                totalOverlap += (overlapEnd - overlapStart);
+            boolean isInTaskPeriod = false;
+            for (TaskTimeInterval taskInterval : taskTimeIntervals) {
+                // 璁$畻鏃堕棿閲嶅彔 --浠诲姟鏃堕棿娈�
+                long overlapStart = Math.max(segmentStart.getTime(), taskInterval.getStartTime().getTime());
+                long overlapEnd = Math.min(segmentEnd.getTime(), taskInterval.getEndTime().getTime());
+                
+                // 濡傛灉鏈夋椂闂撮噸鍙狅紝鍒欒鍒嗘灞炰簬浠诲姟閲岀▼
+                if (overlapEnd > overlapStart) {
+                    isInTaskPeriod = true;
+                    break;
+                }
+            }
+            
+            // 濡傛灉鍒嗘鍦ㄤ换鍔℃椂闂存鍐咃紝鍒欑疮鍔犲叾閲岀▼
+            if (isInTaskPeriod) {
+                totalTaskDistance = totalTaskDistance.add(segment.getSegmentDistance());
             }
         }
         
-        return (double) totalOverlap / segmentDuration;
+        return totalTaskDistance;
+    }
+    /**
+     * 璁$畻鎸囧畾鏃堕棿娈靛唴鐨勫疄闄呬换鍔¢噷绋�
+     * 閫氳繃鏌ユ壘涓庤鏃堕棿娈甸噸鍙犵殑浠诲姟锛屽苟绱姞杩欎簺浠诲姟鍦ㄨ鏃堕棿娈靛唴鐨勫疄闄呴噷绋�
+     */
+    private double calculateActualTaskMileage(Date segmentStart, Date segmentEnd, List<VehicleGpsSegmentMileage> segmentMileages) {
+        if (segmentMileages == null || segmentMileages.isEmpty()) {
+            return 0.0;
+        }
+        
+        double totalTaskMileage = 0.0;
+        
+        // 閬嶅巻鎵�鏈夊垎娈甸噷绋嬫暟鎹紝鎵惧嚭涓庢寚瀹氭椂闂存閲嶅彔涓旀湁鍏宠仈浠诲姟鐨勫垎娈�
+        for (VehicleGpsSegmentMileage segment : segmentMileages) {
+            // 鍙鐞嗘湁鍏宠仈浠诲姟鐨勫垎娈�
+            if (segment.getTaskId() == null) {
+                continue;
+            }
+            
+            // 妫�鏌ュ垎娈垫椂闂翠笌鎸囧畾鏃堕棿娈垫槸鍚︽湁閲嶅彔
+            Date segStart = segment.getSegmentStartTime();
+            Date segEnd = segment.getSegmentEndTime();
+            
+            // 璁$畻閲嶅彔鏃堕棿
+            long overlapStart = Math.max(segmentStart.getTime(), segStart.getTime());
+            long overlapEnd = Math.min(segmentEnd.getTime(), segEnd.getTime());
+            
+            // 濡傛灉鏈夋椂闂撮噸鍙狅紝鍒欏皢璇ュ垎娈电殑璺濈鍔犲叆浠诲姟閲岀▼
+            if (overlapEnd > overlapStart) {
+                if (segment.getSegmentDistance() != null) {
+                    totalTaskMileage += segment.getSegmentDistance().doubleValue();
+                }
+            }
+        }
+        
+        return totalTaskMileage;
     }
 
     /**
@@ -364,6 +433,8 @@
     @Override
     public VehicleMileageStats aggregateFromSegmentMileage(Long vehicleId, Date statDate) {
         try {
+            calculateAndSaveMileageStats(vehicleId, statDate);
+            //TODO
             // 1. 鑾峰彇缁熻鏃ユ湡鐨勫紑濮嬪拰缁撴潫鏃堕棿
             Calendar calendar = Calendar.getInstance();
             calendar.setTime(statDate);
@@ -383,102 +454,23 @@
                 logger.info("杞﹁締ID: {} 鍦ㄦ棩鏈�: {} 鏃犲垎娈甸噷绋嬫暟鎹�", vehicleId, statDate);
                 return null;
             }
-            
+
+            List<TaskTimeInterval> taskIntervals = vehicleMileageStatsMapper.selectTaskTimeIntervals(vehicleId, dayStart, dayEnd);
+            Integer taskCount = taskIntervals.size();
+
+
+            List<VehicleGpsSegmentMileage> mileages = this.getTaskDistanceMileage(vehicleId, dayStart, dayEnd);
+            Integer totalGpsPoints = mileages.stream()
+                .filter(segment -> segment.getGpsPointCount() != null)
+                .mapToInt(VehicleGpsSegmentMileage::getGpsPointCount)
+                .sum();
+            BigDecimal taskDistance = getTaskDistance(taskIntervals,mileages);
+            BigDecimal totalDistance = calculateTotalMileage(segments);
+            BigDecimal nonTaskDistance = totalDistance.subtract(taskDistance);
+            BigDecimal taskRatio = taskDistance.divide(totalDistance, 4, RoundingMode.HALF_UP);
             // 3. 姹囨�婚噷绋嬫暟鎹�
-            BigDecimal totalMileage = BigDecimal.ZERO;
-            int totalGpsPoints = 0;
-            
-            for (VehicleGpsSegmentMileage segment : segments) {
-                if (segment.getSegmentDistance() != null) {
-                    totalMileage = totalMileage.add(segment.getSegmentDistance());
-                }
-                if (segment.getGpsPointCount() != null) {
-                    totalGpsPoints += segment.getGpsPointCount();
-                }
-            }
-            
-            // 4. 璁$畻浠诲姟閲岀▼鍜岄潪浠诲姟閲岀▼锛堜紭鍖栵細浼樺厛浣跨敤task_id鐩存帴鑱氬悎锛�
-            BigDecimal taskMileage = BigDecimal.ZERO;
-            BigDecimal nonTaskMileage = BigDecimal.ZERO;
-            int taskCount = 0;  // 浠诲姟鏁伴噺
-            
-            // 4.1 缁熻鏈塼ask_id鐨勫垎娈垫暟閲�
-            int segmentsWithTask = 0;
-            for (VehicleGpsSegmentMileage segment : segments) {
-                if (segment.getTaskId() != null) {
-                    segmentsWithTask++;
-                }
-            }
-            
-            // 4.2 濡傛灉澶ч儴鍒嗗垎娈甸兘鏈塼ask_id锛屼娇鐢ㄤ紭鍖栨柟妗堬紙鐩存帴鎸塼ask_id鑱氬悎锛�
-            if (segmentsWithTask > segments.size() * 0.8) {
-                logger.debug("杞﹁締ID: {} 鏃ユ湡: {} 浣跨敤浼樺寲鏂规锛氱洿鎺ユ寜task_id鑱氬悎锛坽}涓垎娈垫湁task_id锛屽崰姣攞}%锛�", 
-                           vehicleId, statDate, segmentsWithTask, (segmentsWithTask * 100.0 / segments.size()));
-                
-                // 浣跨敤Set缁熻鍘婚噸鐨勪换鍔D鏁伴噺
-                Set<Long> uniqueTaskIds = new HashSet<>();
-                
-                // 鐩存帴鎸塼ask_id鍒嗙粍鑱氬悎
-                for (VehicleGpsSegmentMileage segment : segments) {
-                    BigDecimal segDistance = segment.getSegmentDistance() != null ? segment.getSegmentDistance() : BigDecimal.ZERO;
-                    
-                    if (segment.getTaskId() != null) {
-                        // 鏈変换鍔D锛岃鍏ヤ换鍔¢噷绋�
-                        taskMileage = taskMileage.add(segDistance);
-                        uniqueTaskIds.add(segment.getTaskId());
-                    } else {
-                        // 娌℃湁浠诲姟ID锛岃鍏ラ潪浠诲姟閲岀▼
-                        nonTaskMileage = nonTaskMileage.add(segDistance);
-                    }
-                }
-                
-                // 璁剧疆鍘婚噸鍚庣殑浠诲姟鏁伴噺
-                taskCount = uniqueTaskIds.size();
-                
-            } else {
-                // 4.3 闄嶇骇鏂规锛氫娇鐢ㄥ師鏈夌殑鏃堕棿閲嶅彔璁$畻鏂瑰紡
-                logger.debug("杞﹁締ID: {} 鏃ユ湡: {} 浣跨敤闄嶇骇鏂规锛氭椂闂撮噸鍙犺绠楋紙鍙湁{}涓垎娈垫湁task_id锛屽崰姣攞}%锛�", 
-                           vehicleId, statDate, segmentsWithTask, (segmentsWithTask * 100.0 / segments.size()));
-                
-                List<TaskTimeInterval> taskIntervals = vehicleMileageStatsMapper.selectTaskTimeIntervals(vehicleId, dayStart, dayEnd);
-                
-                for (VehicleGpsSegmentMileage segment : segments) {
-                    Date segStart = segment.getSegmentStartTime();
-                    Date segEnd = segment.getSegmentEndTime();
-                    BigDecimal segDistance = segment.getSegmentDistance() != null ? segment.getSegmentDistance() : BigDecimal.ZERO;
-                    
-                    // 璁$畻璇ュ垎娈典笌浠诲姟鏃舵鐨勯噸鍙犳瘮渚�
-                    double taskRatio = calculateTaskOverlapRatio(segStart, segEnd, taskIntervals);
-                    
-                    // 鍒嗘憡閲岀▼
-                    BigDecimal taskDist = segDistance.multiply(BigDecimal.valueOf(taskRatio));
-                    BigDecimal nonTaskDist = segDistance.multiply(BigDecimal.valueOf(1 - taskRatio));
-                    
-                    taskMileage = taskMileage.add(taskDist);
-                    nonTaskMileage = nonTaskMileage.add(nonTaskDist);
-                }
-                
-                // 璁剧疆浠诲姟鏁伴噺
-                taskCount = taskIntervals == null ? 0 : taskIntervals.size();
-            }
-            
-            // 璁$畻浠诲姟閲岀▼鍗犳瘮
-            BigDecimal taskRatio = BigDecimal.ZERO;
-            if (totalMileage.compareTo(BigDecimal.ZERO) > 0) {
-                taskRatio = taskMileage.divide(totalMileage, 4, RoundingMode.HALF_UP);
-                
-                // 鏁版嵁鏍¢獙:鍗犳瘮搴斿湪0-1涔嬮棿,濡傛灉瓒呭嚭璇存槑鏁版嵁寮傚父
-                if (taskRatio.compareTo(BigDecimal.ONE) > 0) {
-                    logger.warn("杞﹁締ID: {} 鏃ユ湡: {} 浠诲姟閲岀▼鍗犳瘮寮傚父: {} (浠诲姟閲岀▼:{}, 鎬婚噷绋�:{}), 寮哄埗璁句负1.0", 
-                               vehicleId, statDate, taskRatio, taskMileage, totalMileage);
-                    taskRatio = BigDecimal.ONE;
-                } else if (taskRatio.compareTo(BigDecimal.ZERO) < 0) {
-                    logger.warn("杞﹁締ID: {} 鏃ユ湡: {} 浠诲姟閲岀▼鍗犳瘮涓鸿礋: {}, 寮哄埗璁句负0", 
-                               vehicleId, statDate, taskRatio);
-                    taskRatio = BigDecimal.ZERO;
-                }
-            }
-            
+
+
             // 5. 鏌ヨ鎴栧垱寤虹粺璁¤褰�
             VehicleMileageStats stats = vehicleMileageStatsMapper.selectByVehicleIdAndDate(vehicleId, statDate);
             boolean isNew = (stats == null);
@@ -503,9 +495,9 @@
             }
             
             // 6. 璁剧疆缁熻鏁版嵁
-            stats.setTotalMileage(totalMileage.setScale(2, RoundingMode.HALF_UP));
-            stats.setTaskMileage(taskMileage.setScale(2, RoundingMode.HALF_UP));
-            stats.setNonTaskMileage(nonTaskMileage.setScale(2, RoundingMode.HALF_UP));
+            stats.setTotalMileage(totalDistance.setScale(2, RoundingMode.HALF_UP));
+            stats.setTaskMileage(taskDistance.setScale(2, RoundingMode.HALF_UP));
+            stats.setNonTaskMileage(nonTaskDistance.setScale(2, RoundingMode.HALF_UP));
             stats.setTaskRatio(taskRatio);
             stats.setGpsPointCount(totalGpsPoints);
             stats.setTaskCount(taskCount);
@@ -519,8 +511,8 @@
                 vehicleMileageStatsMapper.updateVehicleMileageStats(stats);
             }
             
-            logger.info("杞﹁締ID: {} 鏃ユ湡: {} 浠庡垎娈垫眹鎬诲畬鎴� - 鎬婚噷绋�: {}km, 浠诲姟閲岀▼: {}km, 闈炰换鍔¢噷绋�: {}km, 鍒嗘鏁�: {}", 
-                       vehicleId, statDate, totalMileage, taskMileage, nonTaskMileage, segments.size());
+//            logger.info("杞﹁締ID: {} 鏃ユ湡: {} 浠庡垎娈垫眹鎬诲畬鎴� - 鎬婚噷绋�: {}km, 浠诲姟閲岀▼: {}km, 闈炰换鍔¢噷绋�: {}km, 鍒嗘鏁�: {}",
+//                       vehicleId, statDate, totalMileage, taskMileage, nonTaskMileage, segments.size());
             
             return stats;
             
diff --git a/ruoyi-system/src/main/resources/mapper/system/NotifyChannelConfigMapper.xml b/ruoyi-system/src/main/resources/mapper/system/NotifyChannelConfigMapper.xml
new file mode 100644
index 0000000..a9fc896
--- /dev/null
+++ b/ruoyi-system/src/main/resources/mapper/system/NotifyChannelConfigMapper.xml
@@ -0,0 +1,91 @@
+<?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.NotifyChannelConfigMapper">
+    
+    <resultMap type="NotifyChannelConfig" id="NotifyChannelConfigResult">
+        <id     property="id"           column="id"/>
+        <result property="notifyType"   column="notify_type"/>
+        <result property="channel"      column="channel"/>
+        <result property="enabled"      column="enabled"/>
+        <result property="priority"     column="priority"/>
+        <result property="configJson"   column="config_json"/>
+        <result property="createBy"     column="create_by"/>
+        <result property="createTime"   column="create_time"/>
+        <result property="updateBy"     column="update_by"/>
+        <result property="updateTime"   column="update_time"/>
+        <result property="remark"       column="remark"/>
+    </resultMap>
+
+    <sql id="selectNotifyChannelConfigVo">
+        select id, notify_type, channel, enabled, priority, config_json,
+               create_by, create_time, update_by, update_time, remark
+        from sys_notify_channel_config
+    </sql>
+
+    <select id="selectNotifyChannelConfigById" parameterType="Long" resultMap="NotifyChannelConfigResult">
+        <include refid="selectNotifyChannelConfigVo"/>
+        where id = #{id}
+    </select>
+
+    <select id="selectNotifyChannelConfigList" parameterType="NotifyChannelConfig" resultMap="NotifyChannelConfigResult">
+        <include refid="selectNotifyChannelConfigVo"/>
+        <where>
+            <if test="notifyType != null and notifyType != ''">
+                AND notify_type = #{notifyType}
+            </if>
+            <if test="channel != null and channel != ''">
+                AND channel = #{channel}
+            </if>
+            <if test="enabled != null and enabled != ''">
+                AND enabled = #{enabled}
+            </if>
+        </where>
+        order by priority desc
+    </select>
+
+    <select id="selectEnabledChannelsByType" parameterType="String" resultMap="NotifyChannelConfigResult">
+        <include refid="selectNotifyChannelConfigVo"/>
+        where notify_type = #{notifyType} and enabled = '1'
+        order by priority desc
+    </select>
+
+    <select id="selectByTypeAndChannel" resultMap="NotifyChannelConfigResult">
+        <include refid="selectNotifyChannelConfigVo"/>
+        where notify_type = #{notifyType} and channel = #{channel}
+    </select>
+
+    <insert id="insertNotifyChannelConfig" parameterType="NotifyChannelConfig" useGeneratedKeys="true" keyProperty="id">
+        insert into sys_notify_channel_config (
+            notify_type, channel, enabled, priority, config_json,
+            create_by, create_time, update_by, update_time, remark
+        ) values (
+            #{notifyType}, #{channel}, #{enabled}, #{priority}, #{configJson},
+            #{createBy}, sysdate(), #{updateBy}, sysdate(), #{remark}
+        )
+    </insert>
+
+    <update id="updateNotifyChannelConfig" parameterType="NotifyChannelConfig">
+        update sys_notify_channel_config
+        <set>
+            <if test="notifyType != null">notify_type = #{notifyType},</if>
+            <if test="channel != null">channel = #{channel},</if>
+            <if test="enabled != null">enabled = #{enabled},</if>
+            <if test="priority != null">priority = #{priority},</if>
+            <if test="configJson != null">config_json = #{configJson},</if>
+            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+            update_time = sysdate()
+        </set>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteNotifyChannelConfigById" parameterType="Long">
+        delete from sys_notify_channel_config where id = #{id}
+    </delete>
+    
+    <delete id="deleteNotifyChannelConfigByIds" parameterType="Long">
+        delete from sys_notify_channel_config where id in 
+        <foreach collection="array" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>
diff --git a/ruoyi-system/src/main/resources/mapper/system/NotifySendLogMapper.xml b/ruoyi-system/src/main/resources/mapper/system/NotifySendLogMapper.xml
new file mode 100644
index 0000000..636a623
--- /dev/null
+++ b/ruoyi-system/src/main/resources/mapper/system/NotifySendLogMapper.xml
@@ -0,0 +1,174 @@
+<?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.NotifySendLogMapper">
+    
+    <resultMap type="NotifySendLog" id="NotifySendLogResult">
+        <result property="id"           column="id"           />
+        <result property="notifyTaskId" column="notify_task_id"/>
+        <result property="taskId"       column="task_id"      />
+        <result property="userId"       column="user_id"      />
+        <result property="userName"     column="user_name"    />
+        <result property="notifyType"   column="notify_type"  />
+        <result property="channel"      column="channel"      />
+        <result property="sendStatus"   column="send_status"  />
+        <result property="sendTime"     column="send_time"    />
+        <result property="sendResult"   column="send_result"  />
+        <result property="responseMsg"  column="response_msg" />
+        <result property="retryCount"   column="retry_count"  />
+        <result property="createTime"   column="create_time"  />
+        <result property="createBy"     column="create_by"    />
+        <result property="updateTime"   column="update_time"  />
+        <result property="updateBy"     column="update_by"    />
+        <result property="remark"       column="remark"       />
+    </resultMap>
+
+    <sql id="selectNotifySendLogVo">
+        select id, notify_task_id, task_id, user_id, user_name, notify_type, channel, send_status, 
+               send_time, send_result, response_msg, retry_count, create_time, create_by, 
+               update_time, update_by, remark
+        from sys_notify_send_log
+    </sql>
+
+    <select id="selectNotifySendLogList" parameterType="NotifySendLog" resultMap="NotifySendLogResult">
+        <include refid="selectNotifySendLogVo"/>
+        <where>
+            <if test="taskId != null">
+                AND task_id = #{taskId}
+            </if>
+            <if test="userId != null">
+                AND user_id = #{userId}
+            </if>
+            <if test="userName != null and userName != ''">
+                AND user_name like concat('%', #{userName}, '%')
+            </if>
+            <if test="notifyType != null and notifyType != ''">
+                AND notify_type = #{notifyType}
+            </if>
+            <if test="channel != null and channel != ''">
+                AND channel = #{channel}
+            </if>
+            <if test="sendStatus != null and sendStatus != ''">
+                AND send_status = #{sendStatus}
+            </if>
+            <if test="params.beginSendTime != null and params.beginSendTime != ''">
+                AND send_time &gt;= #{params.beginSendTime}
+            </if>
+            <if test="params.endSendTime != null and params.endSendTime != ''">
+                AND send_time &lt;= #{params.endSendTime}
+            </if>
+        </where>
+        order by create_time desc
+    </select>
+    
+    <select id="selectNotifySendLogById" parameterType="Long" resultMap="NotifySendLogResult">
+        <include refid="selectNotifySendLogVo"/>
+        where id = #{id}
+    </select>
+
+    <select id="checkNotifySendLogExists" resultType="int">
+        select count(1) from sys_notify_send_log
+        where task_id = #{taskId}
+          and user_id = #{userId}
+          and notify_type = #{notifyType}
+          and channel = #{channel}
+    </select>
+
+    <select id="selectNotifySendLog" resultMap="NotifySendLogResult">
+        <include refid="selectNotifySendLogVo"/>
+        where task_id = #{taskId}
+          and user_id = #{userId}
+          and notify_type = #{notifyType}
+          and channel = #{channel}
+        limit 1
+    </select>
+
+    <select id="selectFailedNotifySendLogs" resultMap="NotifySendLogResult">
+        <include refid="selectNotifySendLogVo"/>
+        where send_status = '2'
+          and retry_count &lt; #{maxRetryCount}
+        order by create_time asc
+        limit 100
+    </select>
+
+    <insert id="insertNotifySendLog" parameterType="NotifySendLog" useGeneratedKeys="true" keyProperty="id">
+        insert into sys_notify_send_log
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="notifyTaskId != null">notify_task_id,</if>
+            <if test="taskId != null">task_id,</if>
+            <if test="userId != null">user_id,</if>
+            <if test="userName != null">user_name,</if>
+            <if test="notifyType != null and notifyType != ''">notify_type,</if>
+            <if test="channel != null and channel != ''">channel,</if>
+            <if test="sendStatus != null">send_status,</if>
+            <if test="sendTime != null">send_time,</if>
+            <if test="sendResult != null">send_result,</if>
+            <if test="responseMsg != null">response_msg,</if>
+            <if test="retryCount != null">retry_count,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="remark != null">remark,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="notifyTaskId != null">#{notifyTaskId},</if>
+            <if test="taskId != null">#{taskId},</if>
+            <if test="userId != null">#{userId},</if>
+            <if test="userName != null">#{userName},</if>
+            <if test="notifyType != null and notifyType != ''">#{notifyType},</if>
+            <if test="channel != null and channel != ''">#{channel},</if>
+            <if test="sendStatus != null">#{sendStatus},</if>
+            <if test="sendTime != null">#{sendTime},</if>
+            <if test="sendResult != null">#{sendResult},</if>
+            <if test="responseMsg != null">#{responseMsg},</if>
+            <if test="retryCount != null">#{retryCount},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="remark != null">#{remark},</if>
+        </trim>
+    </insert>
+
+    <update id="updateNotifySendLog" parameterType="NotifySendLog">
+        update sys_notify_send_log
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="taskId != null">task_id = #{taskId},</if>
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="userName != null">user_name = #{userName},</if>
+            <if test="notifyType != null and notifyType != ''">notify_type = #{notifyType},</if>
+            <if test="channel != null and channel != ''">channel = #{channel},</if>
+            <if test="sendStatus != null">send_status = #{sendStatus},</if>
+            <if test="sendTime != null">send_time = #{sendTime},</if>
+            <if test="sendResult != null">send_result = #{sendResult},</if>
+            <if test="retryCount != null">retry_count = #{retryCount},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="remark != null">remark = #{remark},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <update id="updateSendStatus">
+        update sys_notify_send_log
+        set send_status = #{sendStatus},
+            send_result = #{sendResult},
+            send_time = now(),
+            update_time = now(),
+            retry_count = retry_count + 1
+        where id = #{id}
+    </update>
+
+    <delete id="deleteNotifySendLogById" parameterType="Long">
+        delete from sys_notify_send_log where id = #{id}
+    </delete>
+
+    <delete id="deleteNotifySendLogByIds" parameterType="String">
+        delete from sys_notify_send_log where id in 
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>
diff --git a/ruoyi-system/src/main/resources/mapper/system/NotifyTaskMapper.xml b/ruoyi-system/src/main/resources/mapper/system/NotifyTaskMapper.xml
new file mode 100644
index 0000000..be01049
--- /dev/null
+++ b/ruoyi-system/src/main/resources/mapper/system/NotifyTaskMapper.xml
@@ -0,0 +1,132 @@
+<?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.NotifyTaskMapper">
+    
+    <resultMap type="NotifyTask" id="NotifyTaskResult">
+        <id     property="id"           column="id"/>
+        <result property="taskId"       column="task_id"/>
+        <result property="taskCode"     column="task_code"/>
+        <result property="notifyType"   column="notify_type"/>
+        <result property="userId"       column="user_id"/>
+        <result property="userName"     column="user_name"/>
+        <result property="userPhone"    column="user_phone"/>
+        <result property="title"        column="title"/>
+        <result property="content"      column="content"/>
+        <result property="extraData"    column="extra_data"/>
+        <result property="status"       column="status"/>
+        <result property="retryCount"   column="retry_count"/>
+        <result property="maxRetry"     column="max_retry"/>
+        <result property="errorMsg"     column="error_msg"/>
+        <result property="createBy"     column="create_by"/>
+        <result property="createTime"   column="create_time"/>
+        <result property="updateBy"     column="update_by"/>
+        <result property="updateTime"   column="update_time"/>
+        <result property="remark"       column="remark"/>
+    </resultMap>
+
+    <sql id="selectNotifyTaskVo">
+        select id, task_id, task_code, notify_type, user_id, user_name, user_phone, 
+               title, content, extra_data, status, retry_count, max_retry, error_msg,
+               create_by, create_time, update_by, update_time, remark
+        from sys_notify_task
+    </sql>
+
+    <select id="selectNotifyTaskById" parameterType="Long" resultMap="NotifyTaskResult">
+        <include refid="selectNotifyTaskVo"/>
+        where id = #{id}
+    </select>
+
+    <select id="selectNotifyTaskList" parameterType="NotifyTask" resultMap="NotifyTaskResult">
+        <include refid="selectNotifyTaskVo"/>
+        <where>
+            <if test="taskId != null">
+                AND task_id = #{taskId}
+            </if>
+            <if test="taskCode != null and taskCode != ''">
+                AND task_code = #{taskCode}
+            </if>
+            <if test="notifyType != null and notifyType != ''">
+                AND notify_type = #{notifyType}
+            </if>
+            <if test="userId != null">
+                AND user_id = #{userId}
+            </if>
+            <if test="userName != null and userName != ''">
+                AND user_name like concat('%', #{userName}, '%')
+            </if>
+            <if test="status != null and status != ''">
+                AND status = #{status}
+            </if>
+        </where>
+        order by create_time desc
+    </select>
+
+    <select id="selectPendingNotifyTasks" parameterType="int" resultMap="NotifyTaskResult">
+        <include refid="selectNotifyTaskVo"/>
+        where status = '0' and retry_count &lt; max_retry
+        order by create_time asc
+        limit #{limit}
+    </select>
+
+    <select id="countByTaskUserType" resultType="int">
+        select count(1) from sys_notify_task 
+        where task_id = #{taskId} and user_id = #{userId} and notify_type = #{notifyType}
+    </select>
+
+    <insert id="insertNotifyTask" parameterType="NotifyTask" useGeneratedKeys="true" keyProperty="id">
+        insert into sys_notify_task (
+            task_id, task_code, notify_type, user_id, user_name, user_phone,
+            title, content, extra_data, status, retry_count, max_retry, error_msg,
+            create_by, create_time, update_by, update_time, remark
+        ) values (
+            #{taskId}, #{taskCode}, #{notifyType}, #{userId}, #{userName}, #{userPhone},
+            #{title}, #{content}, #{extraData}, #{status}, #{retryCount}, #{maxRetry}, #{errorMsg},
+            #{createBy}, sysdate(), #{updateBy}, sysdate(), #{remark}
+        )
+    </insert>
+
+    <update id="updateNotifyTask" parameterType="NotifyTask">
+        update sys_notify_task
+        <set>
+            <if test="taskId != null">task_id = #{taskId},</if>
+            <if test="taskCode != null">task_code = #{taskCode},</if>
+            <if test="notifyType != null">notify_type = #{notifyType},</if>
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="userName != null">user_name = #{userName},</if>
+            <if test="userPhone != null">user_phone = #{userPhone},</if>
+            <if test="title != null">title = #{title},</if>
+            <if test="content != null">content = #{content},</if>
+            <if test="extraData != null">extra_data = #{extraData},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="retryCount != null">retry_count = #{retryCount},</if>
+            <if test="maxRetry != null">max_retry = #{maxRetry},</if>
+            <if test="errorMsg != null">error_msg = #{errorMsg},</if>
+            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+            update_time = sysdate()
+        </set>
+        where id = #{id}
+    </update>
+
+    <update id="updateNotifyTaskStatus">
+        update sys_notify_task 
+        set status = #{status}, error_msg = #{errorMsg}, update_time = sysdate()
+        where id = #{id}
+    </update>
+
+    <update id="incrementRetryCount" parameterType="Long">
+        update sys_notify_task 
+        set retry_count = retry_count + 1, update_time = sysdate()
+        where id = #{id}
+    </update>
+
+    <delete id="deleteNotifyTaskById" parameterType="Long">
+        delete from sys_notify_task where id = #{id}
+    </delete>
+
+    <delete id="deleteNotifyTaskByIds" parameterType="String">
+        delete from sys_notify_task where id in 
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>
diff --git a/ruoyi-system/src/main/resources/mapper/system/SysTaskAssigneeMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysTaskAssigneeMapper.xml
index 67422e4..6ecb4d8 100644
--- a/ruoyi-system/src/main/resources/mapper/system/SysTaskAssigneeMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/SysTaskAssigneeMapper.xml
@@ -103,4 +103,8 @@
             #{id}
         </foreach>
     </delete>
+    
+    <delete id="deleteByTaskIdAndUserId">
+        delete from sys_task_assignee where task_id = #{taskId} and user_id = #{userId}
+    </delete>
 </mapper>
diff --git a/ruoyi-system/src/main/resources/mapper/system/SysTaskMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysTaskMapper.xml
index a7efd95..1b5cfbb 100644
--- a/ruoyi-system/src/main/resources/mapper/system/SysTaskMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/SysTaskMapper.xml
@@ -186,6 +186,20 @@
             t.create_time desc
     </select>
 
+    <select id="selectTaskByVehicleIdAndDate" resultMap="SysTaskResult">
+        select tv.task_id, t.actual_start_time,
+        IFNULL(t.actual_end_time, NOW()) as t.actual_end_time
+        from sys_task_vehicle tv
+        inner join sys_task t on tv.task_id = t.task_id
+        where tv.vehicle_id = #{vehicleId}
+        and t.del_flag = '0'
+        and (
+        (t.actual_end_time is not null and t.actual_start_time > #{startTime} and t.actual_end_time &lt; #{endTime} )
+        or (t.actual_end_time is null and t.actual_start_time > ${endTime} )
+        )
+        order by t.actual_start_time
+    </select>
+
     <select id="selectTaskStatistics" resultType="TaskStatisticsVO">
         select 
             count(*) as totalTasks,
diff --git a/ruoyi-system/src/main/resources/mapper/system/VehicleGpsMapper.xml b/ruoyi-system/src/main/resources/mapper/system/VehicleGpsMapper.xml
index 2c78bdf..650f10e 100644
--- a/ruoyi-system/src/main/resources/mapper/system/VehicleGpsMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/VehicleGpsMapper.xml
@@ -130,26 +130,29 @@
         order by collect_time
     </select>
 
-    <!-- 鏌ヨ娲昏穬杞﹁締ID锛堜紭鍖栵細娣诲姞LIMIT闄愬埗锛岄伩鍏嶅ぇ琛ㄦ壂鎻忥級 -->
+    <!-- 鏌ヨ娲昏穬杞﹁締ID锛堜紭鍖栵細浣跨敤GROUP BY鏇夸唬DISTINCT锛屾彁鍗囨�ц兘锛� -->
     <select id="selectActiveVehicleIds" resultType="Long">
-        select distinct vehicle_id
+        select vehicle_id
         from tb_vehicle_gps
         where collect_time &gt;= #{startTime}
+        group by vehicle_id
         order by vehicle_id
     </select>
     
-    <!-- 鏌ヨ鏈璁$畻鐨凣PS鍧愭爣锛堜笉鍦╰b_vehicle_gps_calculated琛ㄤ腑鐨勮褰曪級 -->
+    <!-- 鏌ヨ鏈璁$畻鐨凣PS鍧愭爣锛堜紭鍖栵細浣跨敤NOT EXISTS鏇夸唬LEFT JOIN ... IS NULL锛� -->
     <select id="selectUncalculatedGps" resultMap="VehicleGpsResult">
         SELECT g.gps_id, g.vehicle_id, g.device_id, g.longitude, g.latitude, g.altitude, 
                g.speed, g.direction, g.collect_time, g.device_report_time, 
                g.platform_process_time, g.create_time, v.vehicle_no
         FROM tb_vehicle_gps g
-        LEFT JOIN tb_vehicle_info v ON g.vehicle_id = v.vehicle_id
-        LEFT JOIN tb_vehicle_gps_calculated c ON g.gps_id = c.gps_id
+        INNER JOIN tb_vehicle_info v ON g.vehicle_id = v.vehicle_id
         WHERE g.vehicle_id = #{vehicleId}
           AND g.collect_time &gt;= #{startTime}
           AND g.collect_time &lt;= #{endTime}
-          AND c.gps_id IS NULL  -- 鏈璁$畻鐨凣PS鐐�
+          AND NOT EXISTS (
+              SELECT 1 FROM tb_vehicle_gps_calculated c 
+              WHERE c.gps_id = g.gps_id
+          )
         ORDER BY g.collect_time
     </select>
 </mapper> 
\ No newline at end of file
diff --git a/ruoyi-system/src/main/resources/mapper/system/VehicleMileageStatsMapper.xml b/ruoyi-system/src/main/resources/mapper/system/VehicleMileageStatsMapper.xml
index aee4324..47abb4d 100644
--- a/ruoyi-system/src/main/resources/mapper/system/VehicleMileageStatsMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/VehicleMileageStatsMapper.xml
@@ -88,16 +88,17 @@
     </select>
 
     <select id="selectTaskTimeIntervals" resultMap="TaskTimeIntervalResult">
-        select tv.task_id, t.create_time as start_time, 
+        select tv.task_id, t.actual_start_time as start_time,
                IFNULL(t.actual_end_time, NOW()) as end_time
         from sys_task_vehicle tv
         inner join sys_task t on tv.task_id = t.task_id
         where tv.vehicle_id = #{vehicleId}
           and t.del_flag = '0'
-          and t.actual_end_time is not null
-          and t.create_time &lt; #{endTime}
-          and t.actual_end_time &gt; #{startTime}
-        order by t.create_time
+          and (
+                (t.actual_end_time is not null and t.actual_start_time &gt; #{startTime} and t.actual_end_time &lt; #{endTime})
+                or (t.actual_end_time is null and t.actual_start_time &gt; #{startTime})
+        )
+        order by t.actual_start_time
     </select>
         
     <insert id="insertVehicleMileageStats" parameterType="VehicleMileageStats" useGeneratedKeys="true" keyProperty="statsId">
diff --git a/ruoyi-ui/src/api/system/notify/channelConfig.js b/ruoyi-ui/src/api/system/notify/channelConfig.js
new file mode 100644
index 0000000..b4ca2c0
--- /dev/null
+++ b/ruoyi-ui/src/api/system/notify/channelConfig.js
@@ -0,0 +1,44 @@
+import request from '@/utils/request'
+
+// 鏌ヨ閫氱煡娓犻亾閰嶇疆鍒楄〃
+export function listChannelConfig(query) {
+  return request({
+    url: '/system/notify/channel/config/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鏌ヨ閫氱煡娓犻亾閰嶇疆璇︾粏
+export function getChannelConfig(id) {
+  return request({
+    url: '/system/notify/channel/config/' + id,
+    method: 'get'
+  })
+}
+
+// 鏂板閫氱煡娓犻亾閰嶇疆
+export function addChannelConfig(data) {
+  return request({
+    url: '/system/notify/channel/config',
+    method: 'post',
+    data: data
+  })
+}
+
+// 淇敼閫氱煡娓犻亾閰嶇疆
+export function updateChannelConfig(data) {
+  return request({
+    url: '/system/notify/channel/config',
+    method: 'put',
+    data: data
+  })
+}
+
+// 鍒犻櫎閫氱煡娓犻亾閰嶇疆
+export function delChannelConfig(id) {
+  return request({
+    url: '/system/notify/channel/config/' + id,
+    method: 'delete'
+  })
+}
\ No newline at end of file
diff --git a/ruoyi-ui/src/api/system/notify/log/index.js b/ruoyi-ui/src/api/system/notify/log/index.js
new file mode 100644
index 0000000..4b46fe3
--- /dev/null
+++ b/ruoyi-ui/src/api/system/notify/log/index.js
@@ -0,0 +1,27 @@
+import request from '@/utils/request'
+
+// 鏌ヨ閫氱煡鍙戦�佹棩蹇楀垪琛�
+export function listNotifySendLog(query) {
+  return request({
+    url: '/system/notify/log/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鑾峰彇閫氱煡鍙戦�佹棩蹇楄缁嗕俊鎭�
+export function getNotifySendLog(id) {
+  return request({
+    url: '/system/notify/log/' + id,
+    method: 'get'
+  })
+}
+
+// 瀵煎嚭閫氱煡鍙戦�佹棩蹇�
+export function exportNotifySendLog(query) {
+  return request({
+    url: '/system/notify/log/export',
+    method: 'post',
+    params: query
+  })
+}
\ No newline at end of file
diff --git a/ruoyi-ui/src/api/system/notify/task/index.js b/ruoyi-ui/src/api/system/notify/task/index.js
new file mode 100644
index 0000000..69058da
--- /dev/null
+++ b/ruoyi-ui/src/api/system/notify/task/index.js
@@ -0,0 +1,27 @@
+import request from '@/utils/request'
+
+// 鏌ヨ閫氱煡浠诲姟鍒楄〃
+export function listNotifyTask(query) {
+  return request({
+    url: '/system/notify/task/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鑾峰彇閫氱煡浠诲姟璇︾粏淇℃伅
+export function getNotifyTask(id) {
+  return request({
+    url: '/system/notify/task/' + id,
+    method: 'get'
+  })
+}
+
+// 瀵煎嚭閫氱煡浠诲姟
+export function exportNotifyTask(query) {
+  return request({
+    url: '/system/notify/task/export',
+    method: 'post',
+    params: query
+  })
+}
\ No newline at end of file
diff --git a/ruoyi-ui/src/views/system/mileageStats/index.vue b/ruoyi-ui/src/views/system/mileageStats/index.vue
index 4468509..0cbe724 100644
--- a/ruoyi-ui/src/views/system/mileageStats/index.vue
+++ b/ruoyi-ui/src/views/system/mileageStats/index.vue
@@ -168,8 +168,30 @@
     <!-- 鎵嬪姩缁熻瀵硅瘽妗� -->
     <el-dialog title="鎵嬪姩閲岀▼缁熻" :visible.sync="calculateOpen" width="500px" append-to-body>
       <el-form ref="calculateForm" :model="calculateForm" :rules="calculateRules" label-width="100px">
-        <el-form-item label="杞﹁締ID" prop="vehicleId">
-          <el-input v-model="calculateForm.vehicleId" placeholder="璇疯緭鍏ヨ溅杈咺D" type="number" />
+        <el-form-item label="杞︾墝鍙�" prop="vehicleId">
+          <el-select
+            v-model="calculateForm.vehicleId"
+            placeholder="璇疯緭鍏ヨ溅鐗屽彿鎼滅储"
+            filterable
+            remote
+            :remote-method="searchVehicles"
+            :loading="vehicleSearchLoading"
+            clearable
+            style="width: 100%"
+            @change="handleVehicleChange"
+          >
+            <el-option
+              v-for="vehicle in vehicleOptions"
+              :key="vehicle.vehicleId"
+              :label="vehicle.vehicleNo"
+              :value="vehicle.vehicleId"
+            >
+              <span style="float: left">{{ vehicle.vehicleNo }}</span>
+              <span style="float: right; color: #8492a6; font-size: 13px" v-if="vehicle.deptName">
+                {{ vehicle.deptName }}
+              </span>
+            </el-option>
+          </el-select>
         </el-form-item>
         <el-form-item label="缁熻鏃ユ湡" prop="statDate">
           <el-date-picker
@@ -297,6 +319,7 @@
 <script>
 import { listMileageStats, getMileageStats, delMileageStats, calculateMileageStats, batchCalculateMileageStats, getSegmentsByDateRange } from "@/api/system/mileageStats";
 import { listDept } from "@/api/system/dept";
+import { listVehicle } from "@/api/system/vehicle";
 
 export default {
   name: "MileageStats",
@@ -338,6 +361,10 @@
       calculateLoading: false,
       // 鎵归噺缁熻鍔犺浇鐘舵��
       batchCalculateLoading: false,
+      // 杞﹁締鎼滅储鍔犺浇鐘舵��
+      vehicleSearchLoading: false,
+      // 杞﹁締閫夐」鍒楄〃
+      vehicleOptions: [],
       // 鏌ヨ鍙傛暟
       queryParams: {
         pageNum: 1,
@@ -359,7 +386,7 @@
       // 鎵嬪姩缁熻琛ㄥ崟鏍¢獙
       calculateRules: {
         vehicleId: [
-          { required: true, message: "杞﹁締ID涓嶈兘涓虹┖", trigger: "blur" }
+          { required: true, message: "璇烽�夋嫨杞﹁締", trigger: "change" }
         ],
         statDate: [
           { required: true, message: "缁熻鏃ユ湡涓嶈兘涓虹┖", trigger: "change" }
@@ -413,7 +440,43 @@
     /** 鎵嬪姩缁熻鎸夐挳鎿嶄綔 */
     handleCalculate() {
       this.reset();
+      this.vehicleOptions = [];
       this.calculateOpen = true;
+      // 鎵撳紑瀵硅瘽妗嗘椂鍔犺浇涓�浜涜溅杈嗘暟鎹�
+      this.searchVehicles('');
+    },
+    /** 鎼滅储杞﹁締 */
+    searchVehicles(query) {
+      if (query !== '') {
+        this.vehicleSearchLoading = true;
+        listVehicle({
+          vehicleNo: query,
+          pageNum: 1,
+          pageSize: 50
+        }).then(response => {
+          this.vehicleOptions = response.rows || [];
+          this.vehicleSearchLoading = false;
+        }).catch(() => {
+          this.vehicleSearchLoading = false;
+        });
+      } else {
+        // 濡傛灉鏌ヨ涓虹┖锛屽姞杞藉墠50鏉¤溅杈嗘暟鎹�
+        this.vehicleSearchLoading = true;
+        listVehicle({
+          pageNum: 1,
+          pageSize: 50
+        }).then(response => {
+          this.vehicleOptions = response.rows || [];
+          this.vehicleSearchLoading = false;
+        }).catch(() => {
+          this.vehicleSearchLoading = false;
+        });
+      }
+    },
+    /** 杞﹁締閫夋嫨鍙樺寲 */
+    handleVehicleChange(value) {
+      // 鍙互鍦ㄨ繖閲屾坊鍔犻澶栫殑澶勭悊閫昏緫
+      console.log('閫夋嫨鐨勮溅杈咺D:', value);
     },
     /** 鎵归噺缁熻鎸夐挳鎿嶄綔 */
     handleBatchCalculate() {
diff --git a/ruoyi-ui/src/views/system/notify/channelConfig.vue b/ruoyi-ui/src/views/system/notify/channelConfig.vue
new file mode 100644
index 0000000..f96689c
--- /dev/null
+++ b/ruoyi-ui/src/views/system/notify/channelConfig.vue
@@ -0,0 +1,311 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="閫氱煡绫诲瀷" prop="notifyType">
+        <el-select v-model="queryParams.notifyType" placeholder="璇烽�夋嫨閫氱煡绫诲瀷" clearable>
+          <el-option label="浠诲姟鍒嗛厤" value="TASK_ASSIGN" />
+          <el-option label="鐘舵�佸彉鏇�" value="STATUS_CHANGE" />
+          <el-option label="浠诲姟鍒涘缓" value="TASK_CREATE" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="娓犻亾" prop="channel">
+        <el-select v-model="queryParams.channel" placeholder="璇烽�夋嫨娓犻亾" clearable>
+          <el-option label="寰俊璁㈤槄娑堟伅" value="WECHAT" />
+          <el-option label="鐭俊" value="SMS" />
+          <el-option label="绔欏唴娑堟伅" value="SITE_MSG" />
+          <el-option label="APP鎺ㄩ��" value="APP_PUSH" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鏄惁鍚敤" prop="enabled">
+        <el-select v-model="queryParams.enabled" placeholder="璇烽�夋嫨鍚敤鐘舵��" clearable>
+          <el-option label="鍚敤" value="1" />
+          <el-option label="绂佺敤" value="0" />
+        </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:notify:channel:config: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:notify:channel:config: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:notify:channel:config:remove']"
+        >鍒犻櫎</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="channelConfigList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="ID" align="center" prop="id" />
+      <el-table-column label="閫氱煡绫诲瀷" align="center" prop="notifyType">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.sys_notify_type" :value="scope.row.notifyType"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="娓犻亾" align="center" prop="channel">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.sys_notify_channel" :value="scope.row.channel"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="鏄惁鍚敤" align="center" prop="enabled">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.sys_enabled_disabled" :value="scope.row.enabled"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="浼樺厛绾�" align="center" prop="priority" />
+      <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['system:notify:channel:config:edit']"
+          >淇敼</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['system:notify:channel:config: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="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="閫氱煡绫诲瀷" prop="notifyType">
+          <el-select v-model="form.notifyType" placeholder="璇烽�夋嫨閫氱煡绫诲瀷" style="width: 100%">
+            <el-option label="浠诲姟鍒嗛厤" value="TASK_ASSIGN" />
+            <el-option label="鐘舵�佸彉鏇�" value="STATUS_CHANGE" />
+            <el-option label="浠诲姟鍒涘缓" value="TASK_CREATE" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="娓犻亾" prop="channel">
+          <el-select v-model="form.channel" placeholder="璇烽�夋嫨娓犻亾" style="width: 100%">
+            <el-option label="寰俊璁㈤槄娑堟伅" value="WECHAT" />
+            <el-option label="鐭俊" value="SMS" />
+            <el-option label="绔欏唴娑堟伅" value="SITE_MSG" />
+            <el-option label="APP鎺ㄩ��" value="APP_PUSH" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鏄惁鍚敤" prop="enabled">
+          <el-radio-group v-model="form.enabled">
+            <el-radio label="1">鍚敤</el-radio>
+            <el-radio label="0">绂佺敤</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="浼樺厛绾�" prop="priority">
+          <el-input-number v-model="form.priority" controls-position="right" :min="0" :max="999" />
+        </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 { listChannelConfig, getChannelConfig, addChannelConfig, updateChannelConfig, delChannelConfig } from "@/api/system/notify/channelConfig";
+
+export default {
+  name: "NotifyChannelConfig",
+  dicts: ['sys_notify_type', 'sys_notify_channel', 'sys_enabled_disabled'],
+  data() {
+    return {
+      // 閬僵灞�
+      loading: true,
+      // 閫変腑鏁扮粍
+      ids: [],
+      // 闈炲崟涓鐢�
+      single: true,
+      // 闈炲涓鐢�
+      multiple: true,
+      // 鏄剧ず鎼滅储鏉′欢
+      showSearch: true,
+      // 鎬绘潯鏁�
+      total: 0,
+      // 閫氱煡娓犻亾閰嶇疆琛ㄦ牸鏁版嵁
+      channelConfigList: [],
+      // 寮瑰嚭灞傛爣棰�
+      title: "",
+      // 鏄惁鏄剧ず寮瑰嚭灞�
+      open: false,
+      // 鏌ヨ鍙傛暟
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        notifyType: null,
+        channel: null,
+        enabled: null
+      },
+      // 琛ㄥ崟鍙傛暟
+      form: {},
+      // 琛ㄥ崟鏍¢獙
+      rules: {
+        notifyType: [
+          { required: true, message: "閫氱煡绫诲瀷涓嶈兘涓虹┖", trigger: "change" }
+        ],
+        channel: [
+          { required: true, message: "娓犻亾涓嶈兘涓虹┖", trigger: "change" }
+        ],
+        enabled: [
+          { required: true, message: "鏄惁鍚敤涓嶈兘涓虹┖", trigger: "change" }
+        ],
+        priority: [
+          { required: true, message: "浼樺厛绾т笉鑳戒负绌�", trigger: "blur" }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 鏌ヨ閫氱煡娓犻亾閰嶇疆鍒楄〃 */
+    getList() {
+      this.loading = true;
+      listChannelConfig(this.queryParams).then(response => {
+        this.channelConfigList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 鎼滅储鎸夐挳鎿嶄綔 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 閲嶇疆鎸夐挳鎿嶄綔 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    /** 澶氶�夋閫変腑鏁版嵁 */
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 鏂板鎸夐挳鎿嶄綔 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "娣诲姞閫氱煡娓犻亾閰嶇疆";
+    },
+    /** 淇敼鎸夐挳鎿嶄綔 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getChannelConfig(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "淇敼閫氱煡娓犻亾閰嶇疆";
+      });
+    },
+    /** 鎻愪氦鎸夐挳 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateChannelConfig(this.form).then(response => {
+              this.$modal.msgSuccess("淇敼鎴愬姛");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addChannelConfig(this.form).then(response => {
+              this.$modal.msgSuccess("鏂板鎴愬姛");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 鍒犻櫎鎸夐挳鎿嶄綔 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$modal.confirm('鏄惁纭鍒犻櫎閫氱煡娓犻亾閰嶇疆缂栧彿涓�"' + ids + '"鐨勬暟鎹」锛�').then(function() {
+        return delChannelConfig(ids);
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+      }).catch(() => {});
+    },
+    /** 瀵煎嚭鎸夐挳鎿嶄綔 */
+    handleExport() {
+      this.download('system/notify/channel/config/export', {
+        ...this.queryParams
+      }, `channel_config_${new Date().getTime()}.xlsx`)
+    },
+    /** 琛ㄥ崟閲嶇疆 */
+    reset() {
+      this.form = {
+        id: null,
+        notifyType: null,
+        channel: null,
+        enabled: "1",
+        priority: 0,
+        remark: null
+      };
+      this.resetForm("form");
+    },
+    /** 鍙栨秷鎸夐挳 */
+    cancel() {
+      this.open = false;
+      this.reset();
+    }
+  }
+};
+</script>
\ No newline at end of file
diff --git a/ruoyi-ui/src/views/system/notify/log/index.vue b/ruoyi-ui/src/views/system/notify/log/index.vue
new file mode 100644
index 0000000..fbb1b60
--- /dev/null
+++ b/ruoyi-ui/src/views/system/notify/log/index.vue
@@ -0,0 +1,322 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="浠诲姟ID" prop="taskId">
+        <el-input
+          v-model="queryParams.taskId"
+          placeholder="璇疯緭鍏ヤ换鍔D"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="閫氱煡浠诲姟ID" prop="notifyTaskId">
+        <el-input
+          v-model="queryParams.notifyTaskId"
+          placeholder="璇疯緭鍏ラ�氱煡浠诲姟ID"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="鐢ㄦ埛ID" prop="userId">
+        <el-input
+          v-model="queryParams.userId"
+          placeholder="璇疯緭鍏ョ敤鎴稩D"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="鐢ㄦ埛濮撳悕" prop="userName">
+        <el-input
+          v-model="queryParams.userName"
+          placeholder="璇疯緭鍏ョ敤鎴峰鍚�"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="閫氱煡绫诲瀷" prop="notifyType">
+        <el-select v-model="queryParams.notifyType" placeholder="璇烽�夋嫨閫氱煡绫诲瀷" clearable>
+          <el-option label="浠诲姟鍒嗛厤" value="TASK_ASSIGN" />
+          <el-option label="鐘舵�佸彉鏇�" value="STATUS_CHANGE" />
+          <el-option label="浠诲姟鍒涘缓" value="TASK_CREATE" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="閫氱煡娓犻亾" prop="channel">
+        <el-select v-model="queryParams.channel" placeholder="璇烽�夋嫨閫氱煡娓犻亾" clearable>
+          <el-option label="寰俊璁㈤槄娑堟伅" value="WECHAT" />
+          <el-option label="鐭俊" value="SMS" />
+          <el-option label="绔欏唴娑堟伅" value="SITE_MSG" />
+          <el-option label="APP鎺ㄩ��" value="APP_PUSH" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鍙戦�佺姸鎬�" prop="sendStatus">
+        <el-select v-model="queryParams.sendStatus" placeholder="璇烽�夋嫨鍙戦�佺姸鎬�" clearable>
+          <el-option label="寰呭彂閫�" value="0" />
+          <el-option label="鍙戦�佹垚鍔�" value="1" />
+          <el-option label="鍙戦�佸け璐�" value="2" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鍙戦�佹椂闂�">
+        <el-date-picker
+          v-model="dateRange"
+          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="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['system:notify:log:export']"
+        >瀵煎嚭</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="notifySendLogList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="ID" align="center" prop="id" />
+      <el-table-column label="閫氱煡浠诲姟ID" align="center" prop="notifyTaskId" />
+      <el-table-column label="浠诲姟ID" align="center" prop="taskId" />
+      <el-table-column label="鐢ㄦ埛ID" align="center" prop="userId" />
+      <el-table-column label="鐢ㄦ埛濮撳悕" align="center" prop="userName" />
+      <el-table-column label="閫氱煡绫诲瀷" align="center" prop="notifyType">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.sys_notify_type" :value="scope.row.notifyType"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="閫氱煡娓犻亾" align="center" prop="channel">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.sys_notify_channel" :value="scope.row.channel"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="鍙戦�佺姸鎬�" align="center" prop="sendStatus">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.sys_notify_send_status" :value="scope.row.sendStatus"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="鍙戦�佹椂闂�" align="center" prop="sendTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.sendTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="鍙戦�佺粨鏋�" align="center" prop="sendResult" show-overflow-tooltip />
+      <el-table-column label="閲嶈瘯娆℃暟" align="center" prop="retryCount" />
+      <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleView(scope.row)"
+            v-hasPermi="['system:notify:log:query']"
+          >璇︽儏</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="780px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="閫氱煡浠诲姟ID锛�">{{ form.notifyTaskId }}</el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="浠诲姟ID锛�">{{ form.taskId }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="鐢ㄦ埛ID锛�">{{ form.userId }}</el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鐢ㄦ埛濮撳悕锛�">{{ form.userName }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="閫氱煡绫诲瀷锛�">
+              <dict-tag :options="dict.type.sys_notify_type" :value="form.notifyType"/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="閫氱煡娓犻亾锛�">
+              <dict-tag :options="dict.type.sys_notify_channel" :value="form.channel"/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="鍙戦�佺姸鎬侊細">
+              <dict-tag :options="dict.type.sys_notify_send_status" :value="form.sendStatus"/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="閲嶈瘯娆℃暟锛�">{{ form.retryCount }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="鍙戦�佹椂闂达細">{{ form.sendTime }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="鍙戦�佺粨鏋滐細">{{ form.sendResult }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="鍝嶅簲娑堟伅锛�">{{ form.responseMsg }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="鍒涘缓浜猴細">{{ form.createBy }}</el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鍒涘缓鏃堕棿锛�">{{ form.createTime }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="鏇存柊浜猴細">{{ form.updateBy }}</el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鏇存柊鏃堕棿锛�">{{ form.updateTime }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="澶囨敞锛�">{{ form.remark }}</el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="open = false">鍏� 闂�</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listNotifySendLog, getNotifySendLog } from "@/api/system/notify/log";
+
+export default {
+  name: "NotifySendLog",
+  dicts: ['sys_notify_type', 'sys_notify_channel', 'sys_notify_send_status'],
+  data() {
+    return {
+      // 閬僵灞�
+      loading: true,
+      // 閫変腑鏁扮粍
+      ids: [],
+      // 闈炲崟涓鐢�
+      single: true,
+      // 闈炲涓鐢�
+      multiple: true,
+      // 鏄剧ず鎼滅储鏉′欢
+      showSearch: true,
+      // 鎬绘潯鏁�
+      total: 0,
+      // 閫氱煡鍙戦�佹棩蹇楄〃鏍兼暟鎹�
+      notifySendLogList: [],
+      // 寮瑰嚭灞傛爣棰�
+      title: "",
+      // 鏄惁鏄剧ず寮瑰嚭灞�
+      open: false,
+      // 鏃ユ湡鑼冨洿
+      dateRange: [],
+      // 鏌ヨ鍙傛暟
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        taskId: null,
+        notifyTaskId: null,
+        userId: null,
+        userName: null,
+        notifyType: null,
+        channel: null,
+        sendStatus: null
+      },
+      // 琛ㄥ崟鍙傛暟
+      form: {},
+      // 琛ㄥ崟鏍¢獙
+      rules: {}
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 鏌ヨ閫氱煡鍙戦�佹棩蹇楀垪琛� */
+    getList() {
+      this.loading = true;
+      listNotifySendLog(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+        this.notifySendLogList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 鎼滅储鎸夐挳鎿嶄綔 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 閲嶇疆鎸夐挳鎿嶄綔 */
+    resetQuery() {
+      this.dateRange = [];
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    /** 澶氶�夋閫変腑鏁版嵁 */
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 璇︽儏鎸夐挳鎿嶄綔 */
+    handleView(row) {
+      this.open = true;
+      this.title = "閫氱煡鍙戦�佹棩蹇楄鎯�";
+      const id = row.id || this.ids
+      getNotifySendLog(id).then(response => {
+        this.form = response.data;
+      });
+    },
+    /** 瀵煎嚭鎸夐挳鎿嶄綔 */
+    handleExport() {
+      this.download('system/notify/log/export', {
+        ...this.queryParams
+      }, `notify_send_log_${new Date().getTime()}.xlsx`)
+    }
+  }
+};
+</script>
\ No newline at end of file
diff --git a/ruoyi-ui/src/views/system/notify/task/index.vue b/ruoyi-ui/src/views/system/notify/task/index.vue
new file mode 100644
index 0000000..b56d73c
--- /dev/null
+++ b/ruoyi-ui/src/views/system/notify/task/index.vue
@@ -0,0 +1,310 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="浠诲姟ID" prop="taskId">
+        <el-input
+          v-model="queryParams.taskId"
+          placeholder="璇疯緭鍏ヤ换鍔D"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="浠诲姟缂栧彿" prop="taskCode">
+        <el-input
+          v-model="queryParams.taskCode"
+          placeholder="璇疯緭鍏ヤ换鍔$紪鍙�"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="閫氱煡绫诲瀷" prop="notifyType">
+        <el-select v-model="queryParams.notifyType" placeholder="璇烽�夋嫨閫氱煡绫诲瀷" clearable>
+          <el-option label="浠诲姟鍒嗛厤" value="TASK_ASSIGN" />
+          <el-option label="鐘舵�佸彉鏇�" value="STATUS_CHANGE" />
+          <el-option label="浠诲姟鍒涘缓" value="TASK_CREATE" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鐢ㄦ埛ID" prop="userId">
+        <el-input
+          v-model="queryParams.userId"
+          placeholder="璇疯緭鍏ョ敤鎴稩D"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="鐢ㄦ埛濮撳悕" prop="userName">
+        <el-input
+          v-model="queryParams.userName"
+          placeholder="璇疯緭鍏ョ敤鎴峰鍚�"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="澶勭悊鐘舵��" prop="status">
+        <el-select v-model="queryParams.status" placeholder="璇烽�夋嫨澶勭悊鐘舵��" clearable>
+          <el-option label="寰呭鐞�" value="0" />
+          <el-option label="澶勭悊涓�" value="1" />
+          <el-option label="宸插畬鎴�" value="2" />
+          <el-option label="澶辫触" value="3" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鍒涘缓鏃堕棿">
+        <el-date-picker
+          v-model="dateRange"
+          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="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['system:notify:task:export']"
+        >瀵煎嚭</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="notifyTaskList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="ID" align="center" prop="id" />
+      <el-table-column label="浠诲姟ID" align="center" prop="taskId" />
+      <el-table-column label="浠诲姟缂栧彿" align="center" prop="taskCode" />
+      <el-table-column label="閫氱煡绫诲瀷" align="center" prop="notifyType">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.sys_notify_type" :value="scope.row.notifyType"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="鐢ㄦ埛ID" align="center" prop="userId" />
+      <el-table-column label="鐢ㄦ埛濮撳悕" align="center" prop="userName" />
+      <el-table-column label="鎵嬫満鍙�" align="center" prop="userPhone" />
+      <el-table-column label="鏍囬" align="center" prop="title" />
+      <el-table-column label="鍐呭" align="center" prop="content" show-overflow-tooltip />
+      <el-table-column label="澶勭悊鐘舵��" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.sys_notify_task_status" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="閲嶈瘯娆℃暟" align="center" prop="retryCount" />
+      <el-table-column label="鏈�澶ч噸璇�" align="center" prop="maxRetry" />
+      <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleView(scope.row)"
+            v-hasPermi="['system:notify:task:query']"
+          >璇︽儏</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="780px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="浠诲姟ID锛�">{{ form.taskId }}</el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="浠诲姟缂栧彿锛�">{{ form.taskCode }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="閫氱煡绫诲瀷锛�">
+              <dict-tag :options="dict.type.sys_notify_type" :value="form.notifyType"/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="澶勭悊鐘舵�侊細">
+              <dict-tag :options="dict.type.sys_notify_task_status" :value="form.status"/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="鐢ㄦ埛ID锛�">{{ form.userId }}</el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鐢ㄦ埛濮撳悕锛�">{{ form.userName }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="鎵嬫満鍙凤細">{{ form.userPhone }}</el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="閲嶈瘯娆℃暟锛�">{{ form.retryCount }} / {{ form.maxRetry }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="鏍囬锛�">{{ form.title }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="鍐呭锛�">{{ form.content }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="鎵╁睍鏁版嵁锛�">{{ form.extraData }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="閿欒淇℃伅锛�">{{ form.errorMsg }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="鍒涘缓浜猴細">{{ form.createBy }}</el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鍒涘缓鏃堕棿锛�">{{ form.createTime }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="鏇存柊浜猴細">{{ form.updateBy }}</el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鏇存柊鏃堕棿锛�">{{ form.updateTime }}</el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="澶囨敞锛�">{{ form.remark }}</el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="open = false">鍏� 闂�</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listNotifyTask, getNotifyTask } from "@/api/system/notify/task/index";
+
+export default {
+  name: "NotifyTask",
+  dicts: ['sys_notify_type', 'sys_notify_task_status'],
+  data() {
+    return {
+      // 閬僵灞�
+      loading: true,
+      // 閫変腑鏁扮粍
+      ids: [],
+      // 闈炲崟涓鐢�
+      single: true,
+      // 闈炲涓鐢�
+      multiple: true,
+      // 鏄剧ず鎼滅储鏉′欢
+      showSearch: true,
+      // 鎬绘潯鏁�
+      total: 0,
+      // 閫氱煡浠诲姟琛ㄦ牸鏁版嵁
+      notifyTaskList: [],
+      // 寮瑰嚭灞傛爣棰�
+      title: "",
+      // 鏄惁鏄剧ず寮瑰嚭灞�
+      open: false,
+      // 鏃ユ湡鑼冨洿
+      dateRange: [],
+      // 鏌ヨ鍙傛暟
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        taskId: null,
+        taskCode: null,
+        notifyType: null,
+        userId: null,
+        userName: null,
+        status: null
+      },
+      // 琛ㄥ崟鍙傛暟
+      form: {},
+      // 琛ㄥ崟鏍¢獙
+      rules: {}
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 鏌ヨ閫氱煡浠诲姟鍒楄〃 */
+    getList() {
+      this.loading = true;
+      listNotifyTask(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+        this.notifyTaskList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 鎼滅储鎸夐挳鎿嶄綔 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 閲嶇疆鎸夐挳鎿嶄綔 */
+    resetQuery() {
+      this.dateRange = [];
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    /** 澶氶�夋閫変腑鏁版嵁 */
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 璇︽儏鎸夐挳鎿嶄綔 */
+    handleView(row) {
+      this.open = true;
+      this.title = "閫氱煡浠诲姟璇︽儏";
+      const id = row.id || this.ids
+      getNotifyTask(id).then(response => {
+        this.form = response.data;
+      });
+    },
+    /** 瀵煎嚭鎸夐挳鎿嶄綔 */
+    handleExport() {
+      this.download('system/notify/task/export', {
+        ...this.queryParams
+      }, `notify_task_${new Date().getTime()}.xlsx`)
+    }
+  }
+};
+</script>
\ No newline at end of file
diff --git a/sql/notify_dict.sql b/sql/notify_dict.sql
new file mode 100644
index 0000000..be76d42
--- /dev/null
+++ b/sql/notify_dict.sql
@@ -0,0 +1,51 @@
+-- 閫氱煡绠$悊鐩稿叧瀛楀吀鏁版嵁
+
+-- 1. 閫氱煡绫诲瀷瀛楀吀
+INSERT INTO sys_dict_type(dict_name, dict_type, status, create_by, create_time, remark) 
+VALUES
+('閫氱煡绫诲瀷', 'sys_notify_type', '0', 'admin', SYSDATE(), '閫氱煡绫诲瀷鍒楄〃');
+
+-- 閫氱煡绫诲瀷瀛楀吀鏁版嵁
+INSERT INTO sys_dict_data(dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) 
+VALUES
+(1, '浠诲姟鍒嗛厤', 'TASK_ASSIGN', 'sys_notify_type', '', 'primary', 'Y', '0', 'admin', SYSDATE(), '浠诲姟鍒嗛厤閫氱煡'),
+(2, '鐘舵�佸彉鏇�', 'STATUS_CHANGE', 'sys_notify_type', '', 'success', 'N', '0', 'admin', SYSDATE(), '鐘舵�佸彉鏇撮�氱煡'),
+(3, '浠诲姟鍒涘缓', 'TASK_CREATE', 'sys_notify_type', '', 'info', 'N', '0', 'admin', SYSDATE(), '浠诲姟鍒涘缓閫氱煡');
+
+-- 2. 閫氱煡浠诲姟鐘舵�佸瓧鍏�
+INSERT INTO sys_dict_type(dict_name, dict_type, status, create_by, create_time, remark) 
+VALUES
+('閫氱煡浠诲姟鐘舵��', 'sys_notify_task_status', '0', 'admin', SYSDATE(), '閫氱煡浠诲姟鐘舵�佸垪琛�');
+
+-- 閫氱煡浠诲姟鐘舵�佸瓧鍏告暟鎹�
+INSERT INTO sys_dict_data(dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) 
+VALUES
+(1, '寰呭鐞�', '0', 'sys_notify_task_status', '', 'info', 'Y', '0', 'admin', SYSDATE(), '寰呭鐞�'),
+(2, '澶勭悊涓�', '1', 'sys_notify_task_status', '', 'primary', 'N', '0', 'admin', SYSDATE(), '澶勭悊涓�'),
+(3, '宸插畬鎴�', '2', 'sys_notify_task_status', '', 'success', 'N', '0', 'admin', SYSDATE(), '宸插畬鎴�'),
+(4, '澶辫触', '3', 'sys_notify_task_status', '', 'danger', 'N', '0', 'admin', SYSDATE(), '澶辫触');
+
+-- 3. 閫氱煡娓犻亾瀛楀吀
+INSERT INTO sys_dict_type(dict_name, dict_type, status, create_by, create_time, remark) 
+VALUES
+('閫氱煡娓犻亾', 'sys_notify_channel', '0', 'admin', SYSDATE(), '閫氱煡娓犻亾鍒楄〃');
+
+-- 閫氱煡娓犻亾瀛楀吀鏁版嵁
+INSERT INTO sys_dict_data(dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) 
+VALUES
+(1, '寰俊璁㈤槄娑堟伅', 'WECHAT', 'sys_notify_channel', '', 'primary', 'Y', '0', 'admin', SYSDATE(), '寰俊璁㈤槄娑堟伅'),
+(2, '鐭俊', 'SMS', 'sys_notify_channel', '', 'success', 'N', '0', 'admin', SYSDATE(), '鐭俊'),
+(3, '绔欏唴娑堟伅', 'SITE_MSG', 'sys_notify_channel', '', 'info', 'N', '0', 'admin', SYSDATE(), '绔欏唴娑堟伅'),
+(4, 'APP鎺ㄩ��', 'APP_PUSH', 'sys_notify_channel', '', 'warning', 'N', '0', 'admin', SYSDATE(), 'APP鎺ㄩ��');
+
+-- 4. 閫氱煡鍙戦�佺姸鎬佸瓧鍏�
+INSERT INTO sys_dict_type(dict_name, dict_type, status, create_by, create_time, remark) 
+VALUES
+('閫氱煡鍙戦�佺姸鎬�', 'sys_notify_send_status', '0', 'admin', SYSDATE(), '閫氱煡鍙戦�佺姸鎬佸垪琛�');
+
+-- 閫氱煡鍙戦�佺姸鎬佸瓧鍏告暟鎹�
+INSERT INTO sys_dict_data(dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) 
+VALUES
+(1, '寰呭彂閫�', '0', 'sys_notify_send_status', '', 'info', 'Y', '0', 'admin', SYSDATE(), '寰呭彂閫�'),
+(2, '鍙戦�佹垚鍔�', '1', 'sys_notify_send_status', '', 'success', 'N', '0', 'admin', SYSDATE(), '鍙戦�佹垚鍔�'),
+(3, '鍙戦�佸け璐�', '2', 'sys_notify_send_status', '', 'danger', 'N', '0', 'admin', SYSDATE(), '鍙戦�佸け璐�');
\ No newline at end of file
diff --git a/sql/notify_menu.sql b/sql/notify_menu.sql
new file mode 100644
index 0000000..1b83cdf
--- /dev/null
+++ b/sql/notify_menu.sql
@@ -0,0 +1,53 @@
+-- 閫氱煡绠$悊鑿滃崟鍜屾潈闄怱QL
+
+-- 鑿滃崟 SQL
+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)
+VALUES
+('閫氱煡绠$悊', 1, 9, 'notify', NULL, 1, 0, 'M', '0', '0', '', 'message', 'admin', SYSDATE(), '', NULL, '閫氱煡绠$悊鐩綍');
+
+-- 鑾峰彇鍒氭彃鍏ョ殑鐖惰彍鍗旾D锛堥渶瑕佹墜鍔ㄦ墽琛屼笅闈㈢殑SQL锛屾浛鎹parentId锛�
+SET @parentId = (SELECT menu_id FROM sys_menu WHERE menu_name = '閫氱煡绠$悊' AND menu_type = 'M');
+
+-- 閫氱煡浠诲姟绠$悊鑿滃崟
+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)
+VALUES
+('閫氱煡浠诲姟', @parentId, 1, 'task', 'system/notify/task/index', 1, 0, 'C', '0', '0', 'system:notify:task:list', 'task', 'admin', SYSDATE(), '', NULL, '閫氱煡浠诲姟鑿滃崟');
+
+-- 鑾峰彇鍒氭彃鍏ョ殑閫氱煡浠诲姟鑿滃崟ID
+SET @taskParentId = (SELECT menu_id FROM sys_menu WHERE menu_name = '閫氱煡浠诲姟' AND menu_type = 'C');
+
+-- 閫氱煡浠诲姟鎸夐挳 SQL
+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)
+VALUES
+('閫氱煡浠诲姟鏌ヨ', @taskParentId, 1,  '#', '', 1, 0, 'F', '0', '0', 'system:notify:task:query',        '#', 'admin', SYSDATE(), '', NULL, ''),
+('閫氱煡浠诲姟瀵煎嚭', @taskParentId, 2,  '#', '', 1, 0, 'F', '0', '0', 'system:notify:task:export',       '#', 'admin', SYSDATE(), '', NULL, '');
+
+-- 閫氱煡鍙戦�佹棩蹇楄彍鍗�
+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)
+VALUES
+('鍙戦�佹棩蹇�', @parentId, 2, 'log', 'system/notify/log/index', 1, 0, 'C', '0', '0', 'system:notify:log:list', 'log', 'admin', SYSDATE(), '', NULL, '閫氱煡鍙戦�佹棩蹇楄彍鍗�');
+
+-- 鑾峰彇鍒氭彃鍏ョ殑鍙戦�佹棩蹇楄彍鍗旾D
+SET @logParentId = (SELECT menu_id FROM sys_menu WHERE menu_name = '鍙戦�佹棩蹇�' AND menu_type = 'C');
+
+-- 閫氱煡鍙戦�佹棩蹇楁寜閽� SQL
+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)
+VALUES
+('鍙戦�佹棩蹇楁煡璇�', @logParentId, 1,  '#', '', 1, 0, 'F', '0', '0', 'system:notify:log:query',        '#', 'admin', SYSDATE(), '', NULL, ''),
+('鍙戦�佹棩蹇楀鍑�', @logParentId, 2,  '#', '', 1, 0, 'F', '0', '0', 'system:notify:log:export',       '#', 'admin', SYSDATE(), '', NULL, '');
+
+-- 閫氱煡娓犻亾閰嶇疆鑿滃崟
+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)
+VALUES
+('娓犻亾閰嶇疆', @parentId, 3, 'channelConfig', 'system/notify/channelConfig', 1, 0, 'C', '0', '0', 'system:notify:channel:config:list', 'config', 'admin', SYSDATE(), '', NULL, '閫氱煡娓犻亾閰嶇疆鑿滃崟');
+
+-- 鑾峰彇鍒氭彃鍏ョ殑娓犻亾閰嶇疆鑿滃崟ID
+SET @channelConfigParentId = (SELECT menu_id FROM sys_menu WHERE menu_name = '娓犻亾閰嶇疆' AND menu_type = 'C');
+
+-- 閫氱煡娓犻亾閰嶇疆鎸夐挳 SQL
+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)
+VALUES
+('娓犻亾閰嶇疆鏌ヨ', @channelConfigParentId, 1,  '#', '', 1, 0, 'F', '0', '0', 'system:notify:channel:config:query',        '#', 'admin', SYSDATE(), '', NULL, ''),
+('娓犻亾閰嶇疆鏂板', @channelConfigParentId, 2,  '#', '', 1, 0, 'F', '0', '0', 'system:notify:channel:config:add',       '#', 'admin', SYSDATE(), '', NULL, ''),
+('娓犻亾閰嶇疆淇敼', @channelConfigParentId, 3,  '#', '', 1, 0, 'F', '0', '0', 'system:notify:channel:config:edit',       '#', 'admin', SYSDATE(), '', NULL, ''),
+('娓犻亾閰嶇疆鍒犻櫎', @channelConfigParentId, 4,  '#', '', 1, 0, 'F', '0', '0', 'system:notify:channel:config:remove',       '#', 'admin', SYSDATE(), '', NULL, '');
\ No newline at end of file
diff --git a/sql/sms_config.sql b/sql/sms_config.sql
new file mode 100644
index 0000000..c1b0f43
--- /dev/null
+++ b/sql/sms_config.sql
@@ -0,0 +1,35 @@
+-- ===========================================
+-- 鐭俊鏈嶅姟寮�鍏抽厤缃�
+-- 鐢ㄤ簬鎺у埗绯荤粺鏄惁鍚敤鐭俊閫氱煡鍔熻兘
+-- ===========================================
+
+-- 鎻掑叆鐭俊寮�鍏抽厤缃紙榛樿鍏抽棴锛�
+INSERT INTO sys_config (config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark) 
+VALUES 
+('鐭俊閫氱煡寮�鍏�', 'sms.enabled', 'false', 'N', 'admin', NOW(), 'admin', NOW(), 
+ '鎺у埗鏄惁鍚敤鐭俊閫氱煡鍔熻兘銆倀rue=鍚敤锛宖alse=绂佺敤銆傚叧闂悗绯荤粺灏嗕笉鍐嶅彂閫佷换浣曠煭淇¢�氱煡銆�');
+
+-- ===========================================
+-- 浣跨敤璇存槑锛�
+-- 1. 榛樿鍏抽棴鐭俊鍔熻兘锛岄渶瑕佹墜鍔ㄥ紑鍚�
+-- 2. 寮�鍚墠璇风‘淇濆凡閰嶇疆姝g‘鐨勭煭淇¤处鍙蜂俊鎭�
+-- 3. 鍙湪绯荤粺绠$悊->鍙傛暟璁剧疆涓姩鎬佷慨鏀癸紝鏃犻渶閲嶅惎鏈嶅姟
+--
+-- 閰嶇疆閿鏄庯細
+-- sms.enabled - 鐭俊鏈嶅姟寮�鍏�
+--   true  - 鍚敤鐭俊閫氱煡
+--   false - 绂佺敤鐭俊閫氱煡锛堥粯璁わ級
+--
+-- 鐩稿叧閰嶇疆锛堝湪application.yml涓厤缃級锛�
+-- sms.address   - 鐭俊鏈嶅姟鍦板潃锛堥粯璁わ細sms.izjun.com:8001锛�
+-- sms.userName  - 鐭俊璐﹀彿鐢ㄦ埛鍚�
+-- sms.password  - 鐭俊璐﹀彿瀵嗙爜
+-- sms.signName  - 鐭俊绛惧悕
+-- sms.taskAssignTemplate - 浠诲姟鍒嗛厤閫氱煡妯℃澘
+-- ===========================================
+
+-- 寮�鍚煭淇″姛鑳斤細
+-- UPDATE sys_config SET config_value = 'true' WHERE config_key = 'sms.enabled';
+
+-- 鍏抽棴鐭俊鍔熻兘锛�
+-- UPDATE sys_config SET config_value = 'false' WHERE config_key = 'sms.enabled';
diff --git a/sql/sys_notify_send_log.sql b/sql/sys_notify_send_log.sql
new file mode 100644
index 0000000..fa0f91b
--- /dev/null
+++ b/sql/sys_notify_send_log.sql
@@ -0,0 +1,52 @@
+-- ===========================================
+-- 閫氱煡鍙戦�佽褰曡〃
+-- 鐢ㄤ簬璁板綍鍚勭被閫氱煡娑堟伅鐨勫彂閫佽褰曪紝瀹炵幇闃查噸鏈哄埗
+-- 纭繚鍚屼竴浠诲姟鍚屼竴浜哄悓涓�閫氱煡绫诲瀷鍙彂閫佷竴娆�
+-- ===========================================
+
+DROP TABLE IF EXISTS `sys_notify_send_log`;
+
+CREATE TABLE `sys_notify_send_log` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '涓婚敭ID',
+  `task_id` bigint(20) NOT NULL COMMENT '浠诲姟ID',
+  `user_id` bigint(20) NOT NULL COMMENT '鎺ユ敹鐢ㄦ埛ID',
+  `user_name` varchar(64) DEFAULT NULL COMMENT '鎺ユ敹鐢ㄦ埛濮撳悕',
+  `notify_type` varchar(32) NOT NULL COMMENT '閫氱煡绫诲瀷锛歍ASK_ASSIGN-浠诲姟鍒嗛厤, STATUS_CHANGE-鐘舵�佸彉鏇�, TASK_CREATE-浠诲姟鍒涘缓',
+  `channel` varchar(32) NOT NULL COMMENT '閫氱煡娓犻亾锛歐ECHAT-寰俊璁㈤槄娑堟伅, SMS-鐭俊, APP_PUSH-APP鎺ㄩ��, SITE_MSG-绔欏唴娑堟伅',
+  `send_status` char(1) DEFAULT '0' COMMENT '鍙戦�佺姸鎬侊細0-寰呭彂閫�, 1-鍙戦�佹垚鍔�, 2-鍙戦�佸け璐�',
+  `send_time` datetime DEFAULT NULL COMMENT '鍙戦�佹椂闂�',
+  `send_result` varchar(500) DEFAULT NULL COMMENT '鍙戦�佺粨鏋�/閿欒淇℃伅',
+  `retry_count` int(11) DEFAULT 0 COMMENT '閲嶈瘯娆℃暟',
+  `create_time` datetime NOT NULL COMMENT '鍒涘缓鏃堕棿',
+  `create_by` varchar(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
+  `update_time` datetime DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+  `update_by` varchar(64) DEFAULT '' COMMENT '鏇存柊鑰�',
+  `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_task_user_type_channel` (`task_id`, `user_id`, `notify_type`, `channel`) COMMENT '鍚屼竴浠诲姟鍚屼竴鐢ㄦ埛鍚屼竴绫诲瀷鍚屼竴娓犻亾鍙兘鏈変竴鏉¤褰�',
+  KEY `idx_task_id` (`task_id`),
+  KEY `idx_user_id` (`user_id`),
+  KEY `idx_notify_type` (`notify_type`),
+  KEY `idx_send_status` (`send_status`),
+  KEY `idx_create_time` (`create_time`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='閫氱煡鍙戦�佽褰曡〃';
+
+-- ===========================================
+-- 浣跨敤璇存槑锛�
+-- 1. 鍦ㄥ彂閫侀�氱煡鍓嶏紝鍏堟鏌ユ槸鍚﹀凡瀛樺湪鐩稿悓璁板綍
+-- 2. 濡傛灉涓嶅瓨鍦紝鍒欐彃鍏ヨ褰曞苟鍙戦�侀�氱煡
+-- 3. 鍙戦�佹垚鍔熷悗鏇存柊 send_status 涓� 1
+-- 4. 鍙戦�佸け璐ュ悗鏇存柊 send_status 涓� 2锛屽苟璁板綍閿欒淇℃伅
+-- 5. 鍙互閫氳繃瀹氭椂浠诲姟閲嶈瘯鍙戦�佸け璐ョ殑璁板綍
+--
+-- 閫氱煡绫诲瀷璇存槑锛�
+-- TASK_ASSIGN - 浠诲姟鍒嗛厤閫氱煡锛堟柊浠诲姟鎺ㄩ�佺粰鎵ц浜猴級
+-- STATUS_CHANGE - 浠诲姟鐘舵�佸彉鏇撮�氱煡
+-- TASK_CREATE - 浠诲姟鍒涘缓鎴愬姛閫氱煡
+--
+-- 閫氱煡娓犻亾璇存槑锛�
+-- WECHAT - 寰俊灏忕▼搴忚闃呮秷鎭�
+-- SMS - 鐭俊閫氱煡
+-- APP_PUSH - APP鎺ㄩ�侀�氱煡
+-- SITE_MSG - 绯荤粺绔欏唴娑堟伅
+-- ===========================================
diff --git a/sql/sys_notify_task.sql b/sql/sys_notify_task.sql
new file mode 100644
index 0000000..8a06596
--- /dev/null
+++ b/sql/sys_notify_task.sql
@@ -0,0 +1,95 @@
+-- ===========================================
+-- 閫氱煡浠诲姟涓昏〃
+-- 鐢ㄤ簬缁熶竴绠$悊寰呭彂閫佺殑閫氱煡浠诲姟锛岀敱娑堟伅绯荤粺鍐冲畾鍙戦�佹笭閬�
+-- ===========================================
+
+-- 鍒犻櫎鏃ц〃锛堝鏋滈渶瑕侀噸寤猴級
+-- DROP TABLE IF EXISTS `sys_notify_task`;
+
+CREATE TABLE `sys_notify_task` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '涓婚敭ID',
+  `task_id` bigint(20) NOT NULL COMMENT '鍏宠仈鐨勪笟鍔′换鍔D',
+  `task_code` varchar(64) DEFAULT NULL COMMENT '浠诲姟缂栧彿',
+  `notify_type` varchar(32) NOT NULL COMMENT '閫氱煡绫诲瀷锛歍ASK_ASSIGN-浠诲姟鍒嗛厤, STATUS_CHANGE-鐘舵�佸彉鏇�, TASK_CREATE-浠诲姟鍒涘缓',
+  `user_id` bigint(20) NOT NULL COMMENT '鎺ユ敹鐢ㄦ埛ID',
+  `user_name` varchar(64) DEFAULT NULL COMMENT '鎺ユ敹鐢ㄦ埛濮撳悕',
+  `user_phone` varchar(20) DEFAULT NULL COMMENT '鎺ユ敹鐢ㄦ埛鎵嬫満鍙�',
+  `title` varchar(200) DEFAULT NULL COMMENT '閫氱煡鏍囬',
+  `content` varchar(500) DEFAULT NULL COMMENT '閫氱煡鍐呭',
+  `extra_data` text COMMENT '鎵╁睍鏁版嵁(JSON鏍煎紡锛岀敤浜庢ā鏉垮彉閲忕瓑)',
+  `status` char(1) NOT NULL DEFAULT '0' COMMENT '澶勭悊鐘舵�侊細0-寰呭鐞�, 1-澶勭悊涓�, 2-宸插畬鎴�, 3-澶辫触',
+  `retry_count` int(11) DEFAULT '0' COMMENT '閲嶈瘯娆℃暟',
+  `max_retry` int(11) DEFAULT '3' COMMENT '鏈�澶ч噸璇曟鏁�',
+  `error_msg` varchar(500) DEFAULT NULL COMMENT '閿欒淇℃伅',
+  `create_by` varchar(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
+  `create_time` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `update_by` varchar(64) DEFAULT '' COMMENT '鏇存柊鑰�',
+  `update_time` datetime DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+  `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_task_user_type` (`task_id`, `user_id`, `notify_type`) COMMENT '闃查噸绱㈠紩锛氬悓涓�浠诲姟鍚屼竴鐢ㄦ埛鍚屼竴绫诲瀷鍙兘鏈変竴鏉�',
+  KEY `idx_status` (`status`) COMMENT '鐘舵�佺储寮曪紝鐢ㄤ簬鏌ヨ寰呭鐞嗕换鍔�',
+  KEY `idx_user_id` (`user_id`) COMMENT '鐢ㄦ埛绱㈠紩',
+  KEY `idx_create_time` (`create_time`) COMMENT '鍒涘缓鏃堕棿绱㈠紩'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='閫氱煡浠诲姟涓昏〃';
+
+-- ===========================================
+-- 閫氱煡娓犻亾閰嶇疆琛�
+-- 鐢ㄤ簬閰嶇疆鍚勭被閫氱煡鍚敤鐨勫彂閫佹笭閬�
+-- ===========================================
+
+CREATE TABLE `sys_notify_channel_config` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '涓婚敭ID',
+  `notify_type` varchar(32) NOT NULL COMMENT '閫氱煡绫诲瀷',
+  `channel` varchar(32) NOT NULL COMMENT '娓犻亾锛歐ECHAT-寰俊璁㈤槄娑堟伅, SMS-鐭俊, SITE_MSG-绔欏唴娑堟伅, APP_PUSH-APP鎺ㄩ��',
+  `enabled` char(1) NOT NULL DEFAULT '1' COMMENT '鏄惁鍚敤锛�0-绂佺敤, 1-鍚敤',
+  `priority` int(11) DEFAULT '0' COMMENT '浼樺厛绾э紙鏁板瓧瓒婂ぇ浼樺厛绾ц秺楂橈級',
+  `config_json` text COMMENT '娓犻亾閰嶇疆(JSON鏍煎紡锛屽妯℃澘ID绛�)',
+  `create_by` varchar(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
+  `create_time` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `update_by` varchar(64) DEFAULT '' COMMENT '鏇存柊鑰�',
+  `update_time` datetime DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+  `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_type_channel` (`notify_type`, `channel`) COMMENT '閫氱煡绫诲瀷鍜屾笭閬撳敮涓�'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='閫氱煡娓犻亾閰嶇疆琛�';
+
+-- ===========================================
+-- 鍒濆鍖栭�氱煡娓犻亾閰嶇疆
+-- ===========================================
+
+-- 浠诲姟鍒嗛厤閫氱煡 - 鍚敤寰俊銆佺煭淇°�佺珯鍐呮秷鎭�
+INSERT INTO `sys_notify_channel_config` (`notify_type`, `channel`, `enabled`, `priority`, `config_json`, `create_by`, `create_time`, `remark`) VALUES
+('TASK_ASSIGN', 'SITE_MSG', '1', 100, NULL, 'admin', NOW(), '浠诲姟鍒嗛厤-绔欏唴娑堟伅'),
+('TASK_ASSIGN', 'WECHAT', '1', 90, NULL, 'admin', NOW(), '浠诲姟鍒嗛厤-寰俊璁㈤槄娑堟伅'),
+('TASK_ASSIGN', 'SMS', '0', 80, NULL, 'admin', NOW(), '浠诲姟鍒嗛厤-鐭俊锛堥粯璁ゅ叧闂級');
+
+-- 鐘舵�佸彉鏇撮�氱煡 - 浠呭惎鐢ㄧ珯鍐呮秷鎭�
+INSERT INTO `sys_notify_channel_config` (`notify_type`, `channel`, `enabled`, `priority`, `config_json`, `create_by`, `create_time`, `remark`) VALUES
+('STATUS_CHANGE', 'SITE_MSG', '1', 100, NULL, 'admin', NOW(), '鐘舵�佸彉鏇�-绔欏唴娑堟伅');
+
+-- 浠诲姟鍒涘缓閫氱煡 - 浠呭惎鐢ㄧ珯鍐呮秷鎭�
+INSERT INTO `sys_notify_channel_config` (`notify_type`, `channel`, `enabled`, `priority`, `config_json`, `create_by`, `create_time`, `remark`) VALUES
+('TASK_CREATE', 'SITE_MSG', '1', 100, NULL, 'admin', NOW(), '浠诲姟鍒涘缓-绔欏唴娑堟伅');
+
+-- ===========================================
+-- 淇敼鍙戦�佽褰曡〃锛屽鍔犲叧鑱旈�氱煡浠诲姟涓昏〃鐨勫瓧娈�
+-- ===========================================
+
+-- 濡傛灉 sys_notify_send_log 琛ㄥ凡瀛樺湪锛屾坊鍔� notify_task_id 瀛楁
+ALTER TABLE `sys_notify_send_log` 
+ADD COLUMN `notify_task_id` bigint(20) DEFAULT NULL COMMENT '鍏宠仈鐨勯�氱煡浠诲姟ID' AFTER `id`,
+ADD INDEX `idx_notify_task_id` (`notify_task_id`);
+
+-- ===========================================
+-- 璇存槑锛�
+-- 1. sys_notify_task 鏄�氱煡浠诲姟涓昏〃锛岃褰曢渶瑕佸彂閫佺殑閫氱煡
+-- 2. sys_notify_channel_config 閰嶇疆姣忕閫氱煡绫诲瀷鍚敤鍝簺娓犻亾
+-- 3. sys_notify_send_log 璁板綍姣忎釜娓犻亾鐨勫彂閫佹槑缁�
+--
+-- 宸ヤ綔娴佺▼锛�
+-- 1) 涓氬姟绯荤粺鍒涘缓閫氱煡浠诲姟锛堟彃鍏� sys_notify_task锛�
+-- 2) 閫氱煡鍒嗗彂鏈嶅姟璇诲彇娓犻亾閰嶇疆锛坰ys_notify_channel_config锛�
+-- 3) 鏍规嵁閰嶇疆鍚戝悇娓犻亾鍙戦�侀�氱煡锛屽苟璁板綍鍙戦�佹棩蹇楋紙sys_notify_send_log锛�
+-- 4) 鏇存柊閫氱煡浠诲姟鐘舵��
+-- ===========================================

--
Gitblit v1.9.1