From c5ac97682e3b4ca748541ace97cb37a2295bd81e Mon Sep 17 00:00:00 2001
From: wlzboy <66905212@qq.com>
Date: 星期四, 19 三月 2026 22:46:29 +0800
Subject: [PATCH] feat: 增加GPS清理后台任务

---
 ruoyi-system/src/main/resources/mapper/system/VehicleGpsMapper.xml                  |   18 
 app/pages/index.vue                                                                 |  177 ++++++-----
 ruoyi-system/src/main/java/com/ruoyi/system/domain/enums/TaskStatus.java            |   15 +
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CleanVehicleGpsTask.java           |   34 ++
 GPS 分段里程重复插入问题修复说明.md                                                               |  114 ++++++++
 ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleGpsService.java         |   13 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleGpsMapper.java            |   13 
 app/pages/task/index.vue                                                            |   83 ++++-
 ruoyi-admin/src/main/resources/application.yml                                      |    2 
 remark.md                                                                           |    6 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleGpsServiceImpl.java |   16 +
 app/utils/TaskUtil.js                                                               |  201 ++++++++++++++
 sql/clean_gps.sql                                                                   |   37 +-
 app/pagesTask/detail.vue                                                            |   59 +--
 ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml    |   15 +
 ruoyi-system/src/main/java/com/ruoyi/system/utils/TaskStatusConverter.java          |   37 ++
 16 files changed, 666 insertions(+), 174 deletions(-)

diff --git "a/GPS \345\210\206\346\256\265\351\207\214\347\250\213\351\207\215\345\244\215\346\217\222\345\205\245\351\227\256\351\242\230\344\277\256\345\244\215\350\257\264\346\230\216.md" "b/GPS \345\210\206\346\256\265\351\207\214\347\250\213\351\207\215\345\244\215\346\217\222\345\205\245\351\227\256\351\242\230\344\277\256\345\244\215\350\257\264\346\230\216.md"
new file mode 100644
index 0000000..9427c41
--- /dev/null
+++ "b/GPS \345\210\206\346\256\265\351\207\214\347\250\213\351\207\215\345\244\215\346\217\222\345\205\245\351\227\256\351\242\230\344\277\256\345\244\215\350\257\264\346\230\216.md"
@@ -0,0 +1,114 @@
+# GPS 鍒嗘閲岀▼閲嶅鎻掑叆闂淇璇存槑
+
+## 闂鎻忚堪
+
+鍦ㄦ墽琛� GPS 鍒嗘閲岀▼璁$畻鏃讹紝绯荤粺鎶涘嚭浠ヤ笅寮傚父锛�
+
+```
+org.springframework.jdbc.UncategorizedSQLException: 
+### Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '9-2026-03-15 06:10:00' for key 'tb_vehicle_gps_segment_mileage.uk_vehicle_time'
+```
+
+## 鏍规湰鍘熷洜
+
+鏁版嵁搴撹〃 `tb_vehicle_gps_segment_mileage` 瀹氫箟浜嗗敮涓�绱㈠紩 `uk_vehicle_time (vehicle_id, segment_start_time)`锛岀敤浜庣‘淇濆悓涓�杞﹁締鍦ㄥ悓涓�鏃堕棿娈电殑鍒嗘閲岀▼璁板綍鍙湁涓�鏉°��
+
+铏界劧鍦ㄤ唬鐮佸眰闈紙`VehicleGpsSegmentMileageServiceImpl.java` 绗� 390 琛岋級宸茬粡鏈夋鏌ラ�昏緫锛�
+
+```java
+if (isSegmentAlreadyCalculated(vehicleId, segmentStartTime, segmentGpsList)) {
+    previousSegmentLastPoint = segmentGpsList.get(segmentGpsList.size() - 1);
+    continue;
+}
+```
+
+浣嗗湪**骞跺彂鍦烘櫙**涓嬶紝浠嶅彲鑳藉嚭鐜扮珵鎬佹潯浠讹紙Race Condition锛夛細
+
+1. **绾跨▼ A** 妫�鏌ユ椂闂存 2026-03-15 06:10:00锛屽彂鐜颁笉瀛樺湪
+2. **绾跨▼ B** 鍚屾椂妫�鏌ュ悓涓�鏃堕棿娈碉紝涔熷彂鐜颁笉瀛樺湪
+3. **绾跨▼ A** 鎵ц INSERT
+4. **绾跨▼ B** 灏濊瘯鎵ц INSERT锛岃繚鍙嶅敮涓�绱㈠紩绾︽潫锛屾姏鍑哄紓甯�
+
+## 瑙e喅鏂规
+
+淇敼 MyBatis Mapper XML 鏂囦欢涓殑 INSERT 璇彞锛屼娇鐢� MySQL 鐨� `ON DUPLICATE KEY UPDATE` 璇硶锛�
+
+### 淇敼鏂囦欢
+- `ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml`
+
+### 淇敼鍐呭
+
+鍦ㄥ師鏈夌殑 `insertVehicleGpsSegmentMileage` 鏂规硶涓坊鍔� `ON DUPLICATE KEY UPDATE` 瀛愬彞锛�
+
+```xml
+<insert id="insertVehicleGpsSegmentMileage" parameterType="VehicleGpsSegmentMileage">
+    INSERT INTO tb_vehicle_gps_segment_mileage
+    <trim prefix="(" suffix=")" suffixOverrides=",">
+        <!-- 瀛楁鍒楄〃 -->
+    </trim>
+    <trim prefix="values (" suffix=")" suffixOverrides=",">
+        <!-- 鍊煎垪琛� -->
+    </trim>
+    ON DUPLICATE KEY UPDATE
+    vehicle_no = VALUES(vehicle_no),
+    segment_end_time = VALUES(segment_end_time),
+    start_longitude = VALUES(start_longitude),
+    start_latitude = VALUES(start_latitude),
+    end_longitude = VALUES(end_longitude),
+    end_latitude = VALUES(end_latitude),
+    segment_distance = VALUES(segment_distance),
+    gps_point_count = VALUES(gps_point_count),
+    gps_ids = VALUES(gps_ids),
+    task_id = VALUES(task_id),
+    task_code = VALUES(task_code),
+    calculate_method = VALUES(calculate_method),
+    update_time = NOW()
+</insert>
+```
+
+## 鎶�鏈紭鍔�
+
+浣跨敤 `ON DUPLICATE KEY UPDATE` 鐨勫ソ澶勶細
+
+1. **鍘熷瓙鎬ф搷浣�**锛氭暟鎹簱灞傞潰淇濊瘉鎻掑叆鎴栨洿鏂扮殑鍘熷瓙鎬э紝閬垮厤骞跺彂鍐茬獊
+2. **鏃犻渶棰濆鏌ヨ**锛氫笉闇�瑕佸厛 SELECT 鍐嶅喅瀹� INSERT 杩樻槸 UPDATE
+3. **鎬ц兘鏇翠紭**锛氬噺灏戜竴娆℃暟鎹簱鏌ヨ寮�閿�
+4. **浠g爜绠�娲�**锛氫笉闇�瑕佸鏉傜殑寮傚父鎹曡幏鍜岄噸璇曢�昏緫
+5. **鏁版嵁涓�鑷存��**锛氬鏋滃彂鐢熼噸澶嶏紝鑷姩鏇存柊宸叉湁璁板綍鑰屼笉鏄姤閿�
+
+## 褰卞搷鑼冨洿
+
+- **鍙楀奖鍝嶇殑鍔熻兘**锛欸PS 鍒嗘閲岀▼璁$畻
+- **鍙楀奖鍝嶇殑琛�**锛歚tb_vehicle_gps_segment_mileage`
+- **鍙楀奖鍝嶇殑鎺ュ彛**锛氭墍鏈夎皟鐢� `insertVehicleGpsSegmentMileage` 鐨勬柟娉�
+
+## 娴嬭瘯寤鸿
+
+1. **骞跺彂娴嬭瘯**锛氭ā鎷熷涓嚎绋嬪悓鏃惰绠楀悓涓�杞﹁締鐨� GPS 鍒嗘閲岀▼
+2. **閲嶅鏁版嵁娴嬭瘯**锛氭墜鍔ㄦ瀯閫犻噸澶嶇殑杞﹁締 ID + 鏃堕棿娈靛紑濮嬫椂闂达紝楠岃瘉鏄惁鑳芥纭洿鏂�
+3. **鍥炲綊娴嬭瘯**锛氱‘淇濇甯哥殑鎻掑叆鍔熻兘涓嶅彈褰卞搷
+
+## 閮ㄧ讲姝ラ
+
+1. 閲嶅惎搴旂敤鏈嶅姟鍣ㄥ嵆鍙敓鏁�
+2. 鏃犻渶鎵ц浠讳綍 SQL 鑴氭湰
+3. 鏃犻渶淇敼鏁版嵁搴撹〃缁撴瀯
+
+## 鍚庣画浼樺寲寤鸿
+
+1. **鐩戞帶鏃ュ織**锛氳瀵熸槸鍚﹁繕鏈夊叾浠栧苟鍙戝満鏅鑷寸殑绫讳技闂
+2. **浜嬪姟浼樺寲**锛氬浜庢壒澶勭悊鎿嶄綔锛岃�冭檻娣诲姞閫傚綋鐨勪簨鍔¢殧绂荤骇鍒�
+3. **閿佹満鍒�**锛氬鏋滈棶棰樹粛鐒跺瓨鍦紝鍙互鑰冭檻浣跨敤鏁版嵁搴撹閿佹垨鍒嗗竷寮忛攣
+
+## 鐩稿叧鏂囦欢
+
+- `ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml`
+- `ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleGpsSegmentMileageServiceImpl.java`
+- `ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleGpsSegmentMileage.java`
+- `sql/vehicle_gps_segment_mileage.sql`
+
+---
+
+**淇鏃ユ湡**: 2026-03-16  
+**淇浜哄憳**: AI Assistant  
+**闂绫诲瀷**: 骞跺彂鏁版嵁涓�鑷存��
diff --git a/app/pages/index.vue b/app/pages/index.vue
index ce9d0b4..8d6dd2f 100644
--- a/app/pages/index.vue
+++ b/app/pages/index.vue
@@ -107,24 +107,30 @@
               <view
                 class="task-status"
                 :class="
-                  task.taskStatus === 'PENDING'
+                  task.taskStatus === TaskStatus.PENDING
                     ? 'status-pending'
-                    : task.taskStatus === 'DEPARTING'
+                    : task.taskStatus === TaskStatus.NOT_CONFIRMED
+                    ? 'status-not-confirmed'
+                    : task.taskStatus === TaskStatus.NOT_DEPARTED
+                    ? 'status-not-departed'
+                    : task.taskStatus === TaskStatus.PARTIALLY_CONFIRMED
+                    ? 'status-partially-confirmed'                    
+                    : task.taskStatus === TaskStatus.DEPARTING
                     ? 'status-departing'
-                    : task.taskStatus === 'ARRIVED'
+                    : task.taskStatus === TaskStatus.ARRIVED
                     ? 'status-arrived'
-                    : task.taskStatus === 'RETURNING'
+                    : task.taskStatus === TaskStatus.RETURNING
                     ? 'status-returning'
-                    : task.taskStatus === 'COMPLETED'
+                    : task.taskStatus === TaskStatus.COMPLETED
                     ? 'status-completed'
-                    : task.taskStatus === 'CANCELLED'
+                    : task.taskStatus === TaskStatus.CANCELLED
                     ? 'status-cancelled'
-                    : task.taskStatus === 'IN_PROGRESS'
+                    : task.taskStatus === TaskStatus.IN_PROGRESS
                     ? 'status-in-progress'
-                    : 'status-default'
+                    : 'status-pending'
                 "
               >
-                {{ getStatusText(task.status) }}
+                {{ getStatusText(task.taskStatus) }}
               </view>
             </view>
 
@@ -161,8 +167,8 @@
 
           <!-- 鎿嶄綔鎸夐挳 -->
           <view class="task-actions">
-            <!-- 寰呭鐞嗙姸鎬�: 鏄剧ず鍑哄彂銆佸彇娑� -->
-            <template v-if="task.taskStatus === 'PENDING'">
+            <!-- 寰呭鐞嗙姸鎬侊細鏄剧ず鍑哄彂銆佸彇娑� -->
+            <template v-if="task.taskStatus === TaskStatus.PENDING || task.taskStatus === TaskStatus.NOT_DEPARTED || task.taskStatus === TaskStatus.NOT_CONFIRMED || task.taskStatus === TaskStatus.PARTIALLY_CONFIRMED">
               <button
                 class="action-btn primary"
                 @click="handleTaskAction(task, 'depart')"
@@ -177,8 +183,8 @@
               </button>
             </template>
 
-            <!-- 鍑哄彂涓姸鎬�: 鏄剧ず宸插埌杈俱�佸己鍒剁粨鏉� -->
-            <template v-else-if="task.taskStatus === 'DEPARTING'">
+            <!-- 鍑哄彂涓姸鎬侊細鏄剧ず宸插埌杈俱�佸己鍒剁粨鏉� -->
+            <template v-else-if="task.taskStatus === TaskStatus.DEPARTING">
               <button
                 class="action-btn primary"
                 @click="handleTaskAction(task, 'arrive')"
@@ -193,8 +199,8 @@
               </button>
             </template>
 
-            <!-- 宸插埌杈剧姸鎬�: 鏄剧ず宸茶繑绋� -->
-            <template v-else-if="task.taskStatus === 'ARRIVED'">
+            <!-- 宸插埌杈剧姸鎬侊細鏄剧ず宸茶繑绋� -->
+            <template v-else-if="task.taskStatus === TaskStatus.ARRIVED">
               <button
                 class="action-btn primary"
                 @click="handleTaskAction(task, 'return')"
@@ -203,8 +209,8 @@
               </button>
             </template>
 
-            <!-- 杩旂▼涓姸鎬�: 鏄剧ず宸插畬鎴� -->
-            <template v-else-if="task.taskStatus === 'RETURNING'">
+            <!-- 杩旂▼涓姸鎬侊細鏄剧ず宸插畬鎴� -->
+            <template v-else-if="task.taskStatus === TaskStatus.RETURNING">
               <button
                 class="action-btn primary"
                 @click="handleTaskAction(task, 'complete')"
@@ -265,10 +271,40 @@
 import { formatDateTime } from "@/utils/common";
 import subscribeManager from "@/utils/subscribe";
 import { checkTaskCanDepart } from "@/utils/taskValidator";
+import { getStatusText as getTaskStatusText, TaskStatus } from "@/utils/TaskUtil";
+
+// 浠诲姟绫诲瀷鏄犲皠锛堜复鏃跺畾涔夛紝閬垮厤瀵煎叆闂锛�
+const TASK_TYPE_MAP = {
+  MAINTENANCE: "缁翠慨淇濆吇",
+  FUEL: "鍔犳补",
+  OTHER: "鍏朵粬",
+  EMERGENCY_TRANSFER: "杞繍浠诲姟",
+  WELFARE: "绂忕杞�",
+  maintenance: "缁翠慨淇濆吇",
+  refuel: "鍔犳补",
+  inspection: "宸℃",
+  emergency: "杞繍浠诲姟",
+  welfare: "绂忕杞�"
+};
+
+// 浠诲姟鐘舵�佹槧灏勶紙涓存椂瀹氫箟锛岄伩鍏嶅鍏ラ棶棰橈級
+const TASK_STATUS_MAP = {
+  PENDING: "寰呭鐞�",
+  NOT_CONFIRMED: "瀹屽叏鏈‘璁�",
+  NOT_DEPARTED: "寰呭嚭鍙�",
+  PARTIALLY_CONFIRMED: "閮ㄥ垎纭",
+  DEPARTING: "鍑哄彂涓�",
+  ARRIVED: "宸插埌杈�",
+  RETURNING: "杩旂▼涓�",
+  COMPLETED: "宸插畬鎴�",
+  CANCELLED: "宸插彇娑�",
+  IN_PROGRESS: "浠诲姟涓�"
+};
 
 export default {
   data() {
     return {
+      TaskStatus, // 鏆撮湶 TaskStatus 缁欐ā鏉夸娇鐢�
       // 鐢ㄦ埛缁戝畾鐨勮溅杈嗕俊鎭�
       boundVehicle: "",
       boundVehicleId: null,
@@ -307,13 +343,17 @@
     runningTasks() {
       return this.displayedTaskList.filter((task) => {
         // 鍖呭惈寰呭鐞嗐�佸嚭鍙戜腑銆佸凡鍒拌揪銆佽繑绋嬩腑绛夋墍鏈夋湭瀹屾垚鐨勭姸鎬�
-        return [
-          "PENDING",
-          "DEPARTING",
-          "ARRIVED",
-          "RETURNING",
-          "IN_PROGRESS",
-        ].includes(task.taskStatus);
+        const activeStatuses = [
+          TaskStatus.NOT_CONFIRMED,
+          TaskStatus.NOT_DEPARTED,
+          TaskStatus.PARTIALLY_CONFIRMED,
+          TaskStatus.PENDING,
+          TaskStatus.DEPARTING,
+          TaskStatus.ARRIVED,
+          TaskStatus.RETURNING,
+          TaskStatus.IN_PROGRESS,
+        ];
+        return activeStatuses.includes(task.taskStatus);
       });
     },
     
@@ -651,6 +691,10 @@
     convertStatus(dbStatus) {
       const statusMap = {
         PENDING: "pending",
+        NOT_CONFIRMED: "pending",
+        NOT_DEPARTED: "pending",
+        PARTIALLY_CONFIRMED: "pending",
+        
         DEPARTING: "processing",
         ARRIVED: "processing",
         RETURNING: "processing",
@@ -754,7 +798,7 @@
             this.$modal
               .confirm("纭畾瑕佸嚭鍙戝悧锛�")
               .then(() => {
-                this.updateTaskStatus(task.taskId, "DEPARTING", "浠诲姟宸插嚭鍙�");
+                this.updateTaskStatus(task.taskId, TaskStatus.DEPARTING, "浠诲姟宸插嚭鍙�");
               })
               .catch(() => {});
           } catch (error) {
@@ -764,7 +808,7 @@
             this.$modal
               .confirm("妫�鏌ヤ换鍔$姸鎬佸け璐ワ紝鏄惁缁х画鍑哄彂锛�")
               .then(() => {
-                this.updateTaskStatus(task.taskId, "DEPARTING", "浠诲姟宸插嚭鍙�");
+                this.updateTaskStatus(task.taskId, TaskStatus.DEPARTING, "浠诲姟宸插嚭鍙�");
               })
               .catch(() => {});
           }
@@ -781,7 +825,7 @@
           this.$modal
             .confirm("纭宸插埌杈剧洰鐨勫湴锛�")
             .then(() => {
-              this.updateTaskStatus(task.taskId, "ARRIVED", "宸插埌杈剧洰鐨勫湴");
+              this.updateTaskStatus(task.taskId, TaskStatus.ARRIVED, "宸插埌杈剧洰鐨勫湴");
             })
             .catch(() => {});
           break;
@@ -791,7 +835,7 @@
           this.$modal
             .confirm("纭畾瑕佸己鍒剁粨鏉熸浠诲姟鍚楋紵")
             .then(() => {
-              this.updateTaskStatus(task.taskId, "CANCELLED", "浠诲姟宸插己鍒剁粨鏉�");
+              this.updateTaskStatus(task.taskId, TaskStatus.CANCELLED, "浠诲姟宸插己鍒剁粨鏉�");
             })
             .catch(() => {});
           break;
@@ -801,7 +845,7 @@
           this.$modal
             .confirm("纭寮�濮嬭繑绋嬶紵")
             .then(() => {
-              this.updateTaskStatus(task.taskId, "RETURNING", "宸插紑濮嬭繑绋�");
+              this.updateTaskStatus(task.taskId, TaskStatus.RETURNING, "宸插紑濮嬭繑绋�");
             })
             .catch(() => {});
           break;
@@ -809,7 +853,7 @@
         case "complete":
           // 宸插畬鎴� -> 鐘舵�佸彉涓哄凡瀹屾垚
           // 闇�瑕佹鏌ユ槸鍚︿笂浼犱簡鐭ユ儏鍚屾剰涔�
-          this.checkConsentAttachmentAndThen(task.taskId, "COMPLETED", "浠诲姟宸插畬鎴�");
+          this.checkConsentAttachmentAndThen(task.taskId, TaskStatus.COMPLETED, "浠诲姟宸插畬鎴�");
           break;
       }
     },
@@ -919,14 +963,14 @@
     getLocationAndUpdateStatus(taskId, status, remark, cancelReason) {
       const that = this;
 
-      // 浣跨敤uni.getLocation鑾峰彇GPS浣嶇疆
+      // 浣跨敤 uni.getLocation 鑾峰彇 GPS 浣嶇疆
       uni.getLocation({
         type: "gcj02",
         geocode: true,
         altitude: true,
         success: function (res) {
-          console.log("GPS瀹氫綅鎴愬姛:", res);
-
+          console.log("GPS 瀹氫綅鎴愬姛:", res);
+      
           const statusData = {
             taskStatus: status,
             remark: remark,
@@ -990,55 +1034,14 @@
       });
     },
 
-    // 鑾峰彇鐘舵�佹牱寮忕被
-    getStatusClass(status) {
-      const statusClassMap = {
-        PENDING: "status-pending",
-        DEPARTING: "status-departing",
-        ARRIVED: "status-arrived",
-        RETURNING: "status-returning",
-        COMPLETED: "status-completed",
-        CANCELLED: "status-cancelled",
-        IN_PROGRESS: "status-in-progress",
-      };
-      return statusClassMap[status] || "status-default";
-    },
-
-    getStatusText(status) {
-      // 鏀寔鏂版棫涓ょ鐘舵�佹牸寮�
-      const statusMap = {
-        // 鏂版牸寮忥紙鏁版嵁搴撶姸鎬侊級
-        PENDING: "寰呭鐞�",
-        DEPARTING: "鍑哄彂涓�",
-        ARRIVED: "宸插埌杈�",
-        RETURNING: "杩旂▼涓�",
-        COMPLETED: "宸插畬鎴�",
-        CANCELLED: "宸插彇娑�",
-        IN_PROGRESS: "澶勭悊涓�",
-        // 鏃ф牸寮忥紙UI鐘舵�侊級
-        pending: "寰呭鐞�",
-        processing: "澶勭悊涓�",
-        completed: "宸插畬鎴�",
-      };
-      return statusMap[status] || "鏈煡";
-    },
-
+    // 鑾峰彇浠诲姟绫诲瀷鏂囨湰
     getTaskTypeText(type) {
-      const typeMap = {
-        // 鏂版牸寮忥紙鏁版嵁搴撶被鍨嬶級
-        MAINTENANCE: "缁翠慨淇濆吇",
-        FUEL: "鍔犳补",
-        OTHER: "鍏朵粬",
-        EMERGENCY_TRANSFER: "杞繍浠诲姟",
-        WELFARE: "绂忕杞�",
-        // 鏃ф牸寮忥紙UI绫诲瀷锛�
-        maintenance: "缁翠慨淇濆吇",
-        refuel: "鍔犳补",
-        inspection: "宸℃",
-        emergency: "杞繍浠诲姟",
-        welfare: "绂忕杞�",
-      };
-      return typeMap[type] || "鏈煡绫诲瀷";
+      return TASK_TYPE_MAP[type] || "鏈煡绫诲瀷";
+    },
+
+    // 鑾峰彇浠诲姟鐘舵�佹枃鏈�
+    getStatusText(status) {
+      return TASK_STATUS_MAP[status] || "鏈煡";
     },
 
     clickConfirmsubscribeTaskNotify() {
@@ -1328,6 +1331,18 @@
               background-color: #fff3e0;
               color: #ff9500;
             }
+            &.status-not-confirmed {
+              background-color: #fff3e0;
+              color: #ff9500;
+            }
+            &.status-not-departed {
+              background-color: #fff3e0;
+              color: #ff9500;
+            }
+            &.status-partially-confirmed {
+              background-color: #fff3e0;
+              color: #ff9500;
+            }
 
             // 鍑哄彂涓� - 钃濊壊
             &.status-departing {
diff --git a/app/pages/task/index.vue b/app/pages/task/index.vue
index da47896..244207b 100644
--- a/app/pages/task/index.vue
+++ b/app/pages/task/index.vue
@@ -158,7 +158,7 @@
                       ? 'status-cancelled'
                       : task.taskStatus === 'IN_PROGRESS'
                       ? 'status-in-progress'
-                      : 'status-default'
+                      : 'status-pending'
                   "
                 >
                   {{ getStatusText(task.taskStatus) }}
@@ -197,7 +197,10 @@
             <!-- 鎿嶄綔鎸夐挳 -->
             <view class="task-actions">
               <!-- 寰呭鐞嗙姸鎬�: 鏄剧ず鍑哄彂銆佸彇娑� -->
-              <template v-if="task.taskStatus === 'PENDING'">
+              <template v-if="task.taskStatus === 'PENDING' 
+              || task.taskStatus === 'NOT_DEPARTED' 
+              || task.taskStatus === 'NOT_CONFIRMED' 
+              || task.taskStatus === 'PARTIALLY_CONFIRMED'">
                 <button
                   class="action-btn primary"
                   @click="handleTaskAction(task, 'depart')"
@@ -300,6 +303,35 @@
 import { mapState } from "vuex";
 import { formatDateTime } from "@/utils/common";
 import { checkTaskCanDepart } from "@/utils/taskValidator";
+import { getStatusText as getTaskStatusText, getTaskStatusOptions, getTaskTypeText as getTaskTypeTextUtil } from "@/utils/TaskUtil";
+
+// 浠诲姟绫诲瀷鏄犲皠锛堜复鏃跺畾涔夛紝閬垮厤瀵煎叆闂锛�
+const TASK_TYPE_MAP = {
+  MAINTENANCE: "缁翠慨淇濆吇",
+  FUEL: "鍔犳补",
+  OTHER: "鍏朵粬",
+  EMERGENCY_TRANSFER: "杞繍浠诲姟",
+  WELFARE: "绂忕杞�",
+  maintenance: "缁翠慨淇濆吇",
+  refuel: "鍔犳补",
+  inspection: "宸℃",
+  emergency: "杞繍浠诲姟",
+  welfare: "绂忕杞�"
+};
+
+// 浠诲姟鐘舵�佹槧灏勶紙涓存椂瀹氫箟锛岄伩鍏嶅鍏ラ棶棰橈級
+const TASK_STATUS_MAP = {
+  PENDING: "寰呭鐞�",
+  NOT_CONFIRMED: "瀹屽叏鏈‘璁�",
+  NOT_DEPARTED: "寰呭嚭鍙�",
+  PARTIALLY_CONFIRMED: "閮ㄥ垎纭",
+  DEPARTING: "鍑哄彂涓�",
+  ARRIVED: "宸插埌杈�",
+  RETURNING: "杩旂▼涓�",
+  COMPLETED: "宸插畬鎴�",
+  CANCELLED: "宸插彇娑�",
+  IN_PROGRESS: "浠诲姟涓�"
+};
 
 export default {
   components: {
@@ -315,8 +347,8 @@
         vehicle: "",
         taskNo: "",
       },
-      statusOptions: ["鍏ㄩ儴鐘舵��", "寰呭鐞�", "澶勭悊涓�", "宸插畬鎴�", "宸插彇娑�"],
-      statusValues: ["", "pending", "processing", "completed", "cancelled"],
+      statusOptions: ["鍏ㄩ儴鐘舵��", ...getTaskStatusOptions().map(opt => opt.label)],
+      statusValues: ["", ...getTaskStatusOptions().map(opt => opt.value)],
       selectedStatus: "",
       selectedStatusText: "",
       startDate: "",
@@ -415,6 +447,9 @@
         queryParams.taskStatus = "PENDING";
       } else if (this.currentFilter === "processing") {
         queryParams.taskStatusList = [
+          "NOT_CONFIRMED",
+          "NOT_DEPARTED",
+          "PARTIALLY_CONFIRMED",
           "DEPARTING",
           "ARRIVED",
           "RETURNING",
@@ -429,6 +464,9 @@
           const statusMap = {
             pending: "PENDING",
             processing: [
+              "NOT_CONFIRMED",
+              "NOT_DEPARTED",
+              "PARTIALLY_CONFIRMED",
               "DEPARTING",
               "ARRIVED",
               "RETURNING",
@@ -533,6 +571,9 @@
         queryParams.taskStatus = "PENDING";
       } else if (this.currentFilter === "processing") {
         queryParams.taskStatusList = [
+          "NOT_CONFIRMED",
+          "NOT_DEPARTED",
+          "PARTIALLY_CONFIRMED",
           "DEPARTING",
           "ARRIVED",
           "RETURNING",
@@ -1080,19 +1121,9 @@
       });
     },
 
-    getStatusText(status) {
-      const statusMap = {
-        PENDING: "寰呭鐞�",
-        DEPARTING: "鍑哄彂涓�",
-        ARRIVED: "宸插埌杈�",
-        RETURNING: "杩旂▼涓�",
-        COMPLETED: "宸插畬鎴�",
-        CANCELLED: "宸插彇娑�",
-        IN_PROGRESS: "澶勭悊涓�", // 鍏煎鏃ф暟鎹�
-      };
-      return statusMap[status] || "鏈煡";
-    },
-
+    // 浣跨敤 TaskUtil 涓殑 getStatusText 鏂规硶
+    // getStatusText 宸插湪 utils/TaskUtil.js 涓畾涔�
+    
     // 鑾峰彇鐘舵�佹牱寮忕被
     getStatusClass(status) {
       const statusClassMap = {
@@ -1106,16 +1137,18 @@
       };
       return statusClassMap[status] || "status-default";
     },
+    
+    // 浣跨敤 TaskUtil 涓殑 getTaskTypeText 鏂规硶
+    // getTaskTypeText 宸插湪 utils/TaskUtil.js 涓畾涔�
 
+    // 鑾峰彇浠诲姟绫诲瀷鏂囨湰
     getTaskTypeText(type) {
-      const typeMap = {
-        MAINTENANCE: "缁翠慨淇濆吇",
-        FUEL: "鍔犳补",
-        OTHER: "鍏朵粬",
-        EMERGENCY_TRANSFER: "杞繍浠诲姟",
-        WELFARE: "绂忕杞�",
-      };
-      return typeMap[type] || "鏈煡绫诲瀷";
+      return TASK_TYPE_MAP[type] || "鏈煡绫诲瀷";
+    },
+
+    // 鑾峰彇浠诲姟鐘舵�佹枃鏈�
+    getStatusText(status) {
+      return TASK_STATUS_MAP[status] || "鏈煡";
     },
   },
 };
diff --git a/app/pagesTask/detail.vue b/app/pagesTask/detail.vue
index 4bd3c08..ca3c762 100644
--- a/app/pagesTask/detail.vue
+++ b/app/pagesTask/detail.vue
@@ -71,7 +71,7 @@
             </view>
             <!-- 褰撳墠鐧诲綍浜烘槸璇ユ墽琛屼汉涓旀湭灏辩华鏃舵樉绀哄氨缁寜閽� -->
             <view 
-              v-if="showAssigneeReadyFeature() && isAssigneeSelf(assignee) && !isAssigneeReady(assignee) && taskDetail.taskStatus === 'PENDING'"
+              v-if="showAssigneeReadyFeature() && isAssigneeSelf(assignee) && !isAssigneeReady(assignee) && taskDetail.taskStatus === TaskStatus.PENDING"
               class="assignee-ready-btn"
               :data-user-id="assignee.userId || assignee.oaUserId"
               :data-user-name="assignee.userName"
@@ -275,7 +275,7 @@
       </view>
       
       <!-- 鍙栨秷淇℃伅锛堜粎鍦ㄤ换鍔″凡鍙栨秷涓旀湁鍙栨秷鍘熷洜鏃舵樉绀猴級 -->
-      <view class="detail-section" v-if="taskDetail.taskStatus === 'CANCELLED' && taskDetail.emergencyInfo && taskDetail.emergencyInfo.cancelReason">
+      <view class="detail-section" v-if="taskDetail.taskStatus === TaskStatus.CANCELLED && taskDetail.emergencyInfo && taskDetail.emergencyInfo.cancelReason">
         <view class="section-title">鍙栨秷淇℃伅</view>
         <view class="info-item">
           <view class="label">鍙栨秷鍘熷洜</view>
@@ -465,8 +465,11 @@
     
     <!-- 鎿嶄綔鎸夐挳鍖哄煙 -->
     <view class="action-buttons" v-if="taskDetail">
-      <!-- 寰呭鐞嗙姸鎬�: 鏄剧ず鍑哄彂銆佸彇娑堛�佸己鍒跺畬鎴� -->
-      <template v-if="taskDetail.taskStatus === 'PENDING' ">
+      <!-- 寰呭鐞嗙姸鎬侊細鏄剧ず鍑哄彂銆佸彇娑堛�佸己鍒跺畬鎴� -->
+      <template v-if="taskDetail.taskStatus === TaskStatus.PENDING 
+      || taskDetail.taskStatus === TaskStatus.NOT_DEPARTED 
+      || taskDetail.taskStatus === TaskStatus.NOT_CONFIRMED
+      || taskDetail.taskStatus === TaskStatus.PARTIALLY_CONFIRMED">
         <button 
           v-if="canOperateTask()"
           class="action-btn primary" 
@@ -488,9 +491,9 @@
           寮哄埗瀹屾垚
         </button>
       </template>
-      
-      <!-- 鍑哄彂涓姸鎬�: 鏄剧ず宸插埌杈俱�佸己鍒剁粨鏉熴�佸己鍒跺畬鎴� -->
-      <template v-else-if="taskDetail.taskStatus === 'DEPARTING'">
+          
+      <!-- 鍑哄彂涓姸鎬侊細鏄剧ず宸插埌杈俱�佸己鍒剁粨鏉熴�佸己鍒跺畬鎴� -->
+      <template v-else-if="taskDetail.taskStatus === TaskStatus.DEPARTING">
         <template v-if="canOperateTask()">
           <button 
             class="action-btn primary" 
@@ -514,8 +517,8 @@
         </template>
       </template>
       
-      <!-- 宸插埌杈剧姸鎬�: 鏄剧ず宸茶繑绋� -->
-      <template v-else-if="taskDetail.taskStatus === 'ARRIVED'">
+      <!-- 宸插埌杈剧姸鎬侊細鏄剧ず宸茶繑绋� -->
+      <template v-else-if="taskDetail.taskStatus === TaskStatus.ARRIVED">
         <template v-if="canOperateTask()">
           <button 
             class="action-btn primary" 
@@ -526,8 +529,8 @@
         </template>
       </template>
       
-      <!-- 杩旂▼涓姸鎬�: 鏄剧ず宸插畬鎴� -->
-      <template v-else-if="taskDetail.taskStatus === 'RETURNING'">
+      <!-- 杩旂▼涓姸鎬侊細鏄剧ず宸插畬鎴� -->
+      <template v-else-if="taskDetail.taskStatus === TaskStatus.RETURNING">
         <template v-if="canOperateTask()">
           <button 
             class="action-btn primary" 
@@ -538,8 +541,8 @@
         </template>
       </template>
       
-      <!-- 澶勭悊涓姸鎬�: 鏄剧ず寮哄埗瀹屾垚銆佸彇娑� -->
-      <template v-else-if="taskDetail.taskStatus === 'IN_PROGRESS'">
+      <!-- 澶勭悊涓姸鎬侊細鏄剧ず寮哄埗瀹屾垚銆佸彇娑� -->
+      <template v-else-if="taskDetail.taskStatus === TaskStatus.IN_PROGRESS">
         <template v-if="canOperateTask()">
            <button 
             class="action-btn primary" 
@@ -579,6 +582,7 @@
   import { checkTaskInvoice } from '@/api/invoice'
   import { formatDateTime } from '@/utils/common'
   import { validateTaskForDepart, validateTaskForSettlement, getTaskVehicleId, checkTaskCanDepart } from '@/utils/taskValidator'
+  import { getStatusText as getTaskStatusText, getTaskTypeText as getTaskTypeTextUtil, TaskStatus } from '@/utils/TaskUtil'
   import AttachmentUpload from './components/AttachmentUpload.vue'
   import config from '@/config'
   
@@ -588,6 +592,7 @@
     },
     data() {
       return {
+        TaskStatus, // 鏆撮湶 TaskStatus 缁欐ā鏉夸娇鐢�
         taskDetail: null,
         taskId: null,
         paymentInfo: null, // 鏀粯淇℃伅
@@ -663,10 +668,10 @@
           return ''
         }
         const status = this.taskDetail.taskStatus
-        if (status === 'PENDING') return 'pending'
-        if (['DEPARTING', 'ARRIVED', 'RETURNING', 'IN_PROGRESS'].includes(status)) return 'in_progress'
-        if (status === 'COMPLETED') return 'completed'
-        if (status === 'CANCELLED') return 'cancelled'
+        if (status === TaskStatus.PENDING || status === TaskStatus.NOT_CONFIRMED || status === TaskStatus.NOT_DEPARTED || status === TaskStatus.PARTIALLY_CONFIRMED) return 'pending'
+        if ([TaskStatus.DEPARTING, TaskStatus.ARRIVED, TaskStatus.RETURNING, TaskStatus.IN_PROGRESS].includes(status)) return 'in_progress'
+        if (status === TaskStatus.COMPLETED) return 'completed'
+        if (status === TaskStatus.CANCELLED) return 'cancelled'
         return ''
       },
       // 鏄剧ず璁″垝寮�濮嬫椂闂�
@@ -895,28 +900,12 @@
       
       // 鑾峰彇鐘舵�佹枃鏈�
       getStatusText(status) {
-        const statusMap = {
-          'PENDING': '寰呭鐞�',
-          'DEPARTING': '鍑哄彂涓�',
-          'ARRIVED': '宸插埌杈�',
-          'RETURNING': '杩旂▼涓�',
-          'COMPLETED': '宸插畬鎴�',
-          'CANCELLED': '宸插彇娑�',
-          'IN_PROGRESS': '澶勭悊涓�' // 鍏煎鏃ф暟鎹�
-        }
-        return statusMap[status] || '鏈煡'
+        return getTaskStatusText(status)
       },
       
       // 鑾峰彇浠诲姟绫诲瀷鏂囨湰
       getTaskTypeText(type) {
-        const typeMap = {
-          'MAINTENANCE': '缁翠慨淇濆吇',
-          'FUEL': '鍔犳补',
-          'OTHER': '鍏朵粬',
-          'EMERGENCY_TRANSFER': '杞繍浠诲姟',
-          'WELFARE': '绂忕杞�'
-        }
-        return typeMap[type] || '鏈煡绫诲瀷'
+        return getTaskTypeTextUtil(type)
       },
       
       // 鑾峰彇鐢ㄦ埛绫诲瀷鏍囩
diff --git a/app/utils/TaskUtil.js b/app/utils/TaskUtil.js
new file mode 100644
index 0000000..8dd19dc
--- /dev/null
+++ b/app/utils/TaskUtil.js
@@ -0,0 +1,201 @@
+/**
+ * 浠诲姟宸ュ叿绫�
+ * 鎻愪緵浠诲姟鐩稿叧鐨勯�氱敤宸ュ叿鏂规硶
+ */
+
+/**
+ * 浠诲姟鐘舵�佹灇涓�
+ */
+export const TaskStatus = {
+  /** 寰呭鐞� */
+  PENDING: 'PENDING',
+  
+  /** 閮ㄥ垎纭 */
+  PARTIALLY_CONFIRMED: 'PARTIALLY_CONFIRMED',
+  
+  /** 瀹屽叏鏈‘璁� */
+  NOT_CONFIRMED: 'NOT_CONFIRMED',
+  
+  /** 鏈嚭杞� */
+  NOT_DEPARTED: 'NOT_DEPARTED',
+  
+  /** 鍑哄彂涓� */
+  DEPARTING: 'DEPARTING',
+  
+  /** 宸插埌杈� */
+  ARRIVED: 'ARRIVED',
+  
+  /** 杩旂▼涓� */
+  RETURNING: 'RETURNING',
+  
+  /** 宸插畬鎴� */
+  COMPLETED: 'COMPLETED',
+  
+  /** 宸插彇娑� */
+  CANCELLED: 'CANCELLED',
+  
+  /** 浠诲姟涓� (鍏煎鏃ф暟鎹�) */
+  IN_PROGRESS: 'IN_PROGRESS'
+};
+
+/**
+ * 浠诲姟鐘舵�佹枃鏈槧灏�
+ */
+const STATUS_TEXT_MAP = {
+  [TaskStatus.PENDING]: '寰呭鐞�',
+  [TaskStatus.PARTIALLY_CONFIRMED]: '閮ㄥ垎纭',
+  [TaskStatus.NOT_CONFIRMED]: '瀹屽叏鏈‘璁�',
+  [TaskStatus.NOT_DEPARTED]: '鏈嚭杞�',
+  [TaskStatus.DEPARTING]: '鍑哄彂涓�',
+  [TaskStatus.ARRIVED]: '宸插埌杈�',
+  [TaskStatus.RETURNING]: '杩旂▼涓�',
+  [TaskStatus.COMPLETED]: '宸插畬鎴�',
+  [TaskStatus.CANCELLED]: '宸插彇娑�',
+  [TaskStatus.IN_PROGRESS]: '浠诲姟涓�' // 鍏煎鏃ф暟鎹�
+};
+
+/**
+ * 鑾峰彇浠诲姟鐘舵�佹枃鏈�
+ * @param {string} status - 浠诲姟鐘舵�佷唬鐮�
+ * @returns {string} 浠诲姟鐘舵�佹枃鏈�
+ */
+export function getStatusText(status) {
+  return STATUS_TEXT_MAP[status] || '鏈煡';
+}
+
+/**
+ * 鑾峰彇鎵�鏈変换鍔$姸鎬侀�夐」锛堥�傜敤浜庨�夋嫨鍣級
+ * @returns {Array} 鐘舵�侀�夐」鏁扮粍
+ */
+export function getTaskStatusOptions() {
+  return Object.keys(TaskStatus).map(key => ({
+    value: TaskStatus[key],
+    label: STATUS_TEXT_MAP[TaskStatus[key]]
+  }));
+}
+
+/**
+ * 鍒ゆ柇浠诲姟鏄惁宸插畬鎴�
+ * @param {string} status - 浠诲姟鐘舵�佷唬鐮�
+ * @returns {boolean} 鏄惁宸插畬鎴�
+ */
+export function isTaskCompleted(status) {
+  return status === TaskStatus.COMPLETED;
+}
+
+/**
+ * 鍒ゆ柇浠诲姟鏄惁宸插彇娑�
+ * @param {string} status - 浠诲姟鐘舵�佷唬鐮�
+ * @returns {boolean} 鏄惁宸插彇娑�
+ */
+export function isTaskCancelled(status) {
+  return status === TaskStatus.CANCELLED;
+}
+
+/**
+ * 鍒ゆ柇浠诲姟鏄惁澶勪簬杩涜涓姸鎬侊紙鍖呮嫭寰呭鐞嗐�佸嚭鍙戜腑銆佸凡鍒拌揪銆佽繑绋嬩腑绛夛級
+ * @param {string} status - 浠诲姟鐘舵�佷唬鐮�
+ * @returns {boolean} 鏄惁杩涜涓�
+ */
+export function isTaskInProgress(status) {
+  const activeStatuses = [
+    TaskStatus.PENDING,
+    TaskStatus.PARTIALLY_CONFIRMED,
+    TaskStatus.NOT_CONFIRMED,
+    TaskStatus.NOT_DEPARTED,
+    TaskStatus.DEPARTING,
+    TaskStatus.ARRIVED,
+    TaskStatus.RETURNING,
+    TaskStatus.IN_PROGRESS
+  ];
+  return activeStatuses.includes(status);
+}
+
+/**
+ * 浠诲姟绫诲瀷鏋氫妇
+ */
+export const TaskType = {
+  /** 缁翠慨淇濆吇 */
+  MAINTENANCE: 'MAINTENANCE',
+  
+  /** 鍔犳补 */
+  FUEL: 'FUEL',
+  
+  /** 鍏朵粬 */
+  OTHER: 'OTHER',
+  
+  /** 杞繍浠诲姟 */
+  EMERGENCY_TRANSFER: 'EMERGENCY_TRANSFER',
+  
+  /** 绂忕杞� */
+  WELFARE: 'WELFARE',
+  
+  // 鏃ф牸寮忥紙鍏煎鐢級
+  /** 缁翠慨淇濆吇锛堟棫锛� */
+  maintenance: 'maintenance',
+  
+  /** 鍔犳补锛堟棫锛� */
+  refuel: 'refuel',
+  
+  /** 宸℃锛堟棫锛� */
+  inspection: 'inspection',
+  
+  /** 杞繍浠诲姟锛堟棫锛� */
+  emergency: 'emergency',
+  
+  /** 绂忕杞︼紙鏃э級 */
+  welfare: 'welfare'
+};
+
+/**
+ * 浠诲姟绫诲瀷鏂囨湰鏄犲皠
+ */
+const TYPE_TEXT_MAP = {
+  // 鏂版牸寮忥紙鏁版嵁搴撶被鍨嬶級
+  [TaskType.MAINTENANCE]: '缁翠慨淇濆吇',
+  [TaskType.FUEL]: '鍔犳补',
+  [TaskType.OTHER]: '鍏朵粬',
+  [TaskType.EMERGENCY_TRANSFER]: '杞繍浠诲姟',
+  [TaskType.WELFARE]: '绂忕杞�',
+  // 鏃ф牸寮忥紙UI 绫诲瀷锛�
+  [TaskType.maintenance]: '缁翠慨淇濆吇',
+  [TaskType.refuel]: '鍔犳补',
+  [TaskType.inspection]: '宸℃',
+  [TaskType.emergency]: '杞繍浠诲姟',
+  [TaskType.welfare]: '绂忕杞�'
+};
+
+/**
+ * 鑾峰彇浠诲姟绫诲瀷鏂囨湰
+ * @param {string} type - 浠诲姟绫诲瀷浠g爜
+ * @returns {string} 浠诲姟绫诲瀷鏂囨湰
+ */
+export function getTaskTypeText(type) {
+  return TYPE_TEXT_MAP[type] || '鏈煡绫诲瀷';
+}
+
+/**
+ * 鑾峰彇鎵�鏈変换鍔$被鍨嬮�夐」锛堥�傜敤浜庨�夋嫨鍣級
+ * @returns {Array} 绫诲瀷閫夐」鏁扮粍
+ */
+export function getTaskTypeOptions() {
+  return Object.keys(TaskType).map(key => ({
+    value: TaskType[key],
+    label: TYPE_TEXT_MAP[TaskType[key]]
+  }));
+}
+
+// 榛樿瀵煎嚭
+const TaskUtil = {
+  TaskStatus,
+  TaskType,
+  getStatusText,
+  getTaskStatusOptions,
+  getTaskTypeText,
+  getTaskTypeOptions,
+  isTaskCompleted,
+  isTaskCancelled,
+  isTaskInProgress
+};
+
+export default TaskUtil;
diff --git a/remark.md b/remark.md
new file mode 100644
index 0000000..9798db5
--- /dev/null
+++ b/remark.md
@@ -0,0 +1,6 @@
+鍒嗗叕鍙歌閰嶇疆鍑哄彂鍦板潃
+鏇存柊琛ㄦ暟鎹�
+select ServiceBranch,ServiceAddress,ServiceAddress_lat,ServiceAddress_lng,UnitName,UnitShort,ServiceMinPrice,ServiceUnitPrice,ServiceLong from IntroducerUnitData where UnitState>0 and ServiceAddress_lat is not null and ServiceAddress_lng is not null and ServiceBranch='SZ'
+
+
+update IntroducerUnitData set ServiceAddress='娣卞湷甯傞緳宀楀尯骞虫箹澶ц159鍙峰拰椋庡眳', ServiceAddress_lat=22.684596,ServiceAddress_lng=114.155988 where UnitID=80
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index ffcd5be..fed985b 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -58,7 +58,7 @@
     basename: i18n/messages
   profiles:
     # 鐜 dev|test|prod
-    active: prod
+    active: dev
   # 鏂囦欢涓婁紶
   servlet:
     multipart:
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 f177d1d..e089849 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
@@ -13,17 +13,43 @@
 public class CleanVehicleGpsTask {
     private static final Logger log = LoggerFactory.getLogger(CleanVehicleGpsTask.class);
 
+    /** 姣忔壒鍒犻櫎鏁伴噺 */
+    private static final int BATCH_SIZE = 10000;
+    /** 姣忔壒闂撮殧鏃堕棿锛堟绉掞級锛岄伩鍏嶈繛缁垹闄ゅ鏁版嵁搴撳帇鍔涜繃澶� */
+    private static final long BATCH_INTERVAL_MS = 500;
+
     @Autowired
     private IVehicleGpsService vehicleGpsService;
 
     /**
-     * 娓呯悊杞﹁締GPS鍘嗗彶鏁版嵁
+     * 娓呯悊杞﹁締GPS鍘嗗彶鏁版嵁锛堜繚鐣欐渶杩�2涓湀锛屽垎鎵瑰垹闄わ級
      */
     public void cleanVehicleGpsData() {
         try {
-//            log.info("寮�濮嬫竻鐞嗚溅杈咷PS鍘嗗彶鏁版嵁");
-            int count = vehicleGpsService.deleteVehicleGpsBeforeDate();
-//            log.info("娓呯悊杞﹁締GPS鍘嗗彶鏁版嵁瀹屾垚锛屽叡娓呯悊{}鏉¤褰�", count);
+            log.info("寮�濮嬫竻鐞嗚溅杈咷PS鍘嗗彶鏁版嵁锛堜繚鐣欐渶杩�2涓湀锛屾瘡鎵� {} 鏉★級", BATCH_SIZE);
+            int totalCount = 0;
+            int batchCount;
+            int round = 0;
+            do {
+                batchCount = vehicleGpsService.deleteVehicleGpsBeforeDateBatch(BATCH_SIZE);
+                totalCount += batchCount;
+                round++;
+                log.info("绗� {} 鎵规竻鐞嗗畬鎴愶紝鏈壒鍒犻櫎 {} 鏉★紝宸插垹闄ゅ悎璁� {} 鏉�", round, batchCount, totalCount);
+                if (batchCount == BATCH_SIZE) {
+                    // 杩樻湁鏁版嵁锛岀◢浣滀紤鐪犲啀缁х画
+                    Thread.sleep(BATCH_INTERVAL_MS);
+                }
+            } while (batchCount == BATCH_SIZE);
+
+            log.info("娓呯悊杞﹁締GPS鍘嗗彶鏁版嵁瀹屾垚锛屽叡 {} 鎵癸紝鍒犻櫎 {} 鏉¤褰�", round, totalCount);
+
+            if (totalCount > 0) {
+                log.info("寮�濮嬫墽琛� OPTIMIZE TABLE锛屽洖鏀剁鐩樼┖闂达紙姝ゆ搷浣滆�楁椂杈冮暱锛岃鍕夸腑鏂級");
+                vehicleGpsService.optimizeVehicleGpsTable();
+                log.info("OPTIMIZE TABLE 鎵ц瀹屾垚");
+            } else {
+                log.info("鏈鏃犲彲娓呯悊鐨勮褰曪紝璺宠繃 OPTIMIZE TABLE");
+            }
         } catch (Exception e) {
             log.error("娓呯悊杞﹁締GPS鍘嗗彶鏁版嵁寮傚父", e);
         }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/enums/TaskStatus.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/enums/TaskStatus.java
index e16b50d..3f229ca 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/enums/TaskStatus.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/enums/TaskStatus.java
@@ -9,7 +9,20 @@
     
     /** 寰呭鐞� */
     PENDING("PENDING", "寰呭鐞�"),
-    
+
+    /**
+     * 閮ㄥ垎纭
+     */
+    PARTIALLY_CONFIRMED("PARTIALLY_CONFIRMED", "閮ㄥ垎纭"),
+    /**
+     * 瀹屽叏鏈‘璁�
+     */
+    NOT_CONFIRMED("NOT_CONFIRMED", "瀹屽叏鏈‘璁�"),
+    /**
+     * 鏈嚭杞�
+     */
+    NOT_DEPARTED("NOT_DEPARTED", "鏈嚭杞�"),
+
     /** 鍑哄彂涓� */
     DEPARTING("DEPARTING", "鍑哄彂涓�"),
     
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleGpsMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleGpsMapper.java
index d8afe11..7fab119 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleGpsMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleGpsMapper.java
@@ -52,6 +52,19 @@
     public int deleteVehicleGpsBeforeDate();
 
     /**
+     * 鍒嗘壒鍒犻櫎鎸囧畾鏃ユ湡涔嬪墠鐨勮溅杈咷PS鏁版嵁锛堝甫 LIMIT锛�
+     *
+     * @param batchSize 姣忔壒鍒犻櫎鏉℃暟
+     * @return 鏈壒鍒犻櫎鐨勮褰曟暟
+     */
+    public int deleteVehicleGpsBeforeDateBatch(@Param("batchSize") int batchSize);
+
+    /**
+     * 浼樺寲琛紝鍥炴敹DELETE鍚庣殑纾佺洏纰庣墖绌洪棿
+     */
+    public void optimizeVehicleGpsTable();
+
+    /**
      * 鏌ヨ杞﹁締鍦ㄦ寚瀹氭椂闂磋寖鍥村唴鐨凣PS鏁版嵁锛堟寜閲囬泦鏃堕棿鎺掑簭锛�
      * 
      * @param vehicleId 杞﹁締ID
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleGpsService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleGpsService.java
index 970c3ac..6e07ff8 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleGpsService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleGpsService.java
@@ -43,4 +43,17 @@
      * @return 鍒犻櫎鐨勮褰曟暟
      */
     public int deleteVehicleGpsBeforeDate();
+
+    /**
+     * 鍒嗘壒鍒犻櫎鎸囧畾鏃ユ湡涔嬪墠鐨勮溅杈咷PS鏁版嵁
+     *
+     * @param batchSize 姣忔壒鍒犻櫎鏉℃暟
+     * @return 鏈壒鍒犻櫎鐨勮褰曟暟
+     */
+    public int deleteVehicleGpsBeforeDateBatch(int batchSize);
+
+    /**
+     * 浼樺寲琛紝鍥炴敹DELETE鍚庣殑纾佺洏纰庣墖绌洪棿
+     */
+    public void optimizeVehicleGpsTable();
 } 
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleGpsServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleGpsServiceImpl.java
index ad9eb5a..96ce0d7 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleGpsServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleGpsServiceImpl.java
@@ -79,4 +79,20 @@
     public int deleteVehicleGpsBeforeDate() {
         return vehicleGpsMapper.deleteVehicleGpsBeforeDate();
     }
+
+    /**
+     * 鍒嗘壒鍒犻櫎鎸囧畾鏃ユ湡涔嬪墠鐨勮溅杈咷PS鏁版嵁
+     */
+    @Override
+    public int deleteVehicleGpsBeforeDateBatch(int batchSize) {
+        return vehicleGpsMapper.deleteVehicleGpsBeforeDateBatch(batchSize);
+    }
+
+    /**
+     * 浼樺寲琛紝鍥炴敹DELETE鍚庣殑纾佺洏纰庣墖绌洪棿
+     */
+    @Override
+    public void optimizeVehicleGpsTable() {
+        vehicleGpsMapper.optimizeVehicleGpsTable();
+    }
 } 
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/TaskStatusConverter.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/TaskStatusConverter.java
index eb82fa1..593052e 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/utils/TaskStatusConverter.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/TaskStatusConverter.java
@@ -45,11 +45,13 @@
         
         switch (legacyStatusCode) {
             case 0:  // 鏂拌皟搴﹀崟锛堟湭涓嬪彂锛�
-            case 1:  // 瀹屽叏鏈‘璁�
-            case 2:  // 閮ㄥ垎宸茬‘璁�
-            case 3:  // 鏈嚭杞�
                 return TaskStatus.PENDING;
-                
+            case 1:  // 瀹屽叏鏈‘璁�
+                return TaskStatus.NOT_CONFIRMED;
+            case 2:  // 閮ㄥ垎宸茬‘璁�
+                return TaskStatus.PARTIALLY_CONFIRMED;
+            case 3:  // 鏈嚭杞�
+                return TaskStatus.NOT_DEPARTED;
             case 4:  // 宸插嚭杞︼紙鍘绘帴鎮h�呴�斾腑锛�
                 return TaskStatus.DEPARTING;
                 
@@ -85,11 +87,32 @@
             log.warn("鏂扮郴缁熶换鍔$姸鎬佷负绌�");
             return null;
         }
-        
+        /**
+         * 0	0 - 鏂拌皟搴﹀崟锛堟湭涓嬪彂锛�
+         * 1	1 - 瀹屽叏鏈‘璁�
+         * 2	2 - 閮ㄥ垎宸茬‘璁�
+         * 3	鏈嚭杞�
+         * 4	3 - 宸插嚭杞︼紙鍘绘帴鎮h�呴�斾腑锛�
+         * 5	宸插嚭杞︼紙绛夊緟鎮h�咃級
+         * 6	4 - 宸插嚭杞︼紙鏈嶅姟涓級
+         * 7	5 - 宸查�佽揪锛堝洖绋嬩腑锛�
+         * 8	宸茶繑鍥�
+         * 9	璺戠┖鍗曪紝宸茶繑鍥�
+         * 10	鍙栨秷
+         * 11	宸叉彁浜わ紝绛夊緟瀹℃牳
+         * 12	瀹℃牳瀹屾垚
+         * 13	瀹℃牳涓嶉�氳繃
+         * 14	宸查┗鐐�
+         */
         switch (taskStatus) {
-            case PENDING:
+            case NOT_DEPARTED:
                 return 3;  // 鏈嚭杞�
-                
+            case NOT_CONFIRMED:
+                return 1;  // 瀹屽叏鏈‘璁�
+            case PARTIALLY_CONFIRMED:
+                return 2;  // 閮ㄥ垎纭
+            case PENDING:
+                return 0;  // 鏈嚭杞�
             case DEPARTING:
                 return 4;  // 宸插嚭杞︼紙鍘绘帴鎮h�呴�斾腑锛�
                 
diff --git a/ruoyi-system/src/main/resources/mapper/system/VehicleGpsMapper.xml b/ruoyi-system/src/main/resources/mapper/system/VehicleGpsMapper.xml
index 0aa55dd..984d7f1 100644
--- a/ruoyi-system/src/main/resources/mapper/system/VehicleGpsMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/VehicleGpsMapper.xml
@@ -112,14 +112,20 @@
     </select>
 
     <delete id="deleteVehicleGpsBeforeDate">
-        delete g from tb_vehicle_gps g
-        where g.collect_time &lt; (
-            select date_sub(max(collect_time), interval 2 day)
-            from tb_vehicle_gps g2
-            where g2.vehicle_id = g.vehicle_id
-        )
+        delete from tb_vehicle_gps
+        where collect_time &lt; DATE_SUB(NOW(), INTERVAL 2 MONTH)
     </delete>
 
+    <delete id="deleteVehicleGpsBeforeDateBatch">
+        delete from tb_vehicle_gps
+        where collect_time &lt; DATE_SUB(NOW(), INTERVAL 2 MONTH)
+        LIMIT #{batchSize}
+    </delete>
+
+    <update id="optimizeVehicleGpsTable">
+        OPTIMIZE TABLE tb_vehicle_gps
+    </update>
+
     <select id="selectGpsDataByTimeRange" resultMap="VehicleGpsResult">
         select gps_id, vehicle_id, device_id, longitude, latitude, altitude, speed, direction, 
                collect_time, device_report_time, platform_process_time, create_time
diff --git a/ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml b/ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml
index c56d5d6..8b6f6c8 100644
--- a/ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml
@@ -99,6 +99,7 @@
         WHERE task_id = #{taskId}
     </select>
         
+    <!-- 鎻掑叆鎴栨洿鏂板垎娈甸噷绋嬭褰曪紙閬垮厤閲嶅鎻掑叆锛� -->
     <insert id="insertVehicleGpsSegmentMileage" parameterType="VehicleGpsSegmentMileage">
         INSERT INTO tb_vehicle_gps_segment_mileage
         <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -133,6 +134,20 @@
             <if test="taskCode != null">#{taskCode},</if>
             <if test="calculateMethod != null">#{calculateMethod},</if>
         </trim>
+        ON DUPLICATE KEY UPDATE
+        vehicle_no = VALUES(vehicle_no),
+        segment_end_time = VALUES(segment_end_time),
+        start_longitude = VALUES(start_longitude),
+        start_latitude = VALUES(start_latitude),
+        end_longitude = VALUES(end_longitude),
+        end_latitude = VALUES(end_latitude),
+        segment_distance = VALUES(segment_distance),
+        gps_point_count = VALUES(gps_point_count),
+        gps_ids = VALUES(gps_ids),
+        task_id = VALUES(task_id),
+        task_code = VALUES(task_code),
+        calculate_method = VALUES(calculate_method),
+        update_time = NOW()
     </insert>
 
     <update id="updateVehicleGpsSegmentMileage" parameterType="VehicleGpsSegmentMileage">
diff --git a/sql/clean_gps.sql b/sql/clean_gps.sql
index 692b8e8..1ec063b 100644
--- a/sql/clean_gps.sql
+++ b/sql/clean_gps.sql
@@ -1,27 +1,36 @@
+-- ----------------------------
+-- 娓呯悊杞﹁締GPS鍘嗗彶鏁版嵁瀹氭椂浠诲姟閰嶇疆
+-- 璇存槑锛氭瘡澶╁噷鏅�2鐐规墽琛岋紝淇濈暀鏈�杩�2涓湀鏁版嵁锛岃秴鍑洪儴鍒嗗垎鎵瑰垹闄わ紙姣忔壒1涓囨潯锛�
+-- ----------------------------
+
+-- 濡傛灉宸插瓨鍦ㄥ悓鍚嶄换鍔″厛鍒犻櫎锛岄伩鍏嶉噸澶嶆彃鍏�
+DELETE FROM sys_job 
+WHERE job_name = '娓呯悊杞﹁締GPS鍘嗗彶鏁版嵁' AND job_group = 'DEFAULT';
+
 INSERT INTO sys_job (
-    job_name, 
-    job_group, 
-    invoke_target, 
-    cron_expression, 
-    misfire_policy, 
-    concurrent, 
-    status, 
-    create_by, 
-    create_time, 
-    update_by, 
-    update_time, 
+    job_name,
+    job_group,
+    invoke_target,
+    cron_expression,
+    misfire_policy,
+    concurrent,
+    status,
+    create_by,
+    create_time,
+    update_by,
+    update_time,
     remark
 ) VALUES (
     '娓呯悊杞﹁締GPS鍘嗗彶鏁版嵁',
     'DEFAULT',
     'cleanVehicleGpsTask.cleanVehicleGpsData()',
-    '0 0 1 * * ?',
-    '1',
+    '0 0 2 * * ?',
+    '3',
     '1',
     '0',
     'admin',
     sysdate(),
     'admin',
     sysdate(),
-    '姣忓ぉ鍑屾櫒1鐐规竻鐞嗚溅杈咷PS鍘嗗彶鏁版嵁锛屽彧淇濈暀姣忓彴杞︽渶鍚�2澶╃殑鏁版嵁'
+    '姣忓ぉ鍑屾櫒2鐐规墽琛岋紝鍒嗘壒鍒犻櫎tb_vehicle_gps涓秴杩�2涓湀鐨勫巻鍙叉暟鎹紙姣忔壒1涓囨潯锛屾壒娆¢棿闅�500ms锛夛紝鍒犻櫎瀹屾垚鍚庢墽琛孫PTIMIZE TABLE鍥炴敹纾佺洏绌洪棿'
 );
\ No newline at end of file

--
Gitblit v1.9.1