From 4f2925f1974844b66225ac70ae35065b8262b315 Mon Sep 17 00:00:00 2001
From: wlzboy <66905212@qq.com>
Date: 星期四, 04 十二月 2025 13:26:11 +0800
Subject: [PATCH] feat:增加微信token缓存

---
 app/api/system/user.js                                                                                  |   12 
 ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml                                         |    1 
 app/pagesTask/create-emergency.vue                                                                      |   24 +
 ruoyi-ui/src/views/system/mileageStats/index.vue                                                        |  103 ++++
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleMileageStatsServiceImpl.java            |   73 ++
 prd/StaffSelector组件分公司用户加载说明.md                                                                         |  485 ++++++++++++++++++++++
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatTaskNotifyServiceImpl.java               |   80 +++
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskServiceImpl.java                        |   14 
 sql/remove_wechat_token_fields.sql                                                                      |    8 
 ruoyi-ui/src/api/system/mileageStats.js                                                                 |   13 
 app/pagesTask/components/StaffSelector.vue                                                              |  113 ++++
 ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/BranchUserQueryVO.java                            |   32 +
 app/pagesTask/edit-emergency.vue                                                                        |    7 
 sql/fix_segment_mileage_task_association.sql                                                            |  134 ++++++
 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java                             |    5 
 dryad-payment/src/main/java/com/ruoyi/payment/infrastructure/channel/alipay/AlipayThirdPartyClient.java |   16 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java                        |   19 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatLoginServiceImpl.java                    |   48 ++
 sql/add_wechat_token_fields.sql                                                                         |    9 
 sql/test_mileage_stats_optimization.sql                                                                 |  110 +++++
 20 files changed, 1,234 insertions(+), 72 deletions(-)

diff --git a/app/api/system/user.js b/app/api/system/user.js
index 1ea6103..8ea2b48 100644
--- a/app/api/system/user.js
+++ b/app/api/system/user.js
@@ -56,3 +56,15 @@
     method: 'get'
   })
 }
+
+// 鏍规嵁鍒嗗叕鍙窱D鍒楄〃鏌ヨ鐢ㄦ埛锛堝皬绋嬪簭绔笓鐢級
+// 鏍规嵁鍒嗗叕鍙窱D鏁扮粍鏌ヨ鐢ㄦ埛(POST鏂瑰紡)
+export function listUsersByBranchDepts(branchDeptIds) {
+  return request({
+    url: '/system/user/branch/users',
+    method: 'post',
+    data: {
+      branchDeptIds
+    }
+  })
+}
diff --git a/app/pagesTask/components/StaffSelector.vue b/app/pagesTask/components/StaffSelector.vue
index 55c5391..9f750ed 100644
--- a/app/pagesTask/components/StaffSelector.vue
+++ b/app/pagesTask/components/StaffSelector.vue
@@ -109,7 +109,7 @@
 <script>
 import { mapState } from 'vuex'
 import uniPopup from '@/uni_modules/uni-popup/components/uni-popup/uni-popup.vue'
-import { listBranchUsers } from "@/api/system/user"
+import { listUsersByBranchDepts } from "@/api/system/user"
 
 export default {
   name: 'StaffSelector',
@@ -141,6 +141,16 @@
     currentUserRemovable: {
       type: Boolean,
       default: false
+    },
+    // 鍒嗗叕鍙窱D鍒楄〃锛堝閮ㄤ紶鍏ワ紝鐢ㄤ簬鎸囧畾鍔犺浇鍝簺鍒嗗叕鍙哥殑鐢ㄦ埛锛�
+    branchDeptIds: {
+      type: Array,
+      default: null
+    },
+    // 鍗曚釜鍒嗗叕鍙窱D锛堜粎浼犱竴涓椂鏇翠究鎹凤級
+    branchDeptId: {
+      type: [Number, String],
+      default: null
     }
   },
   data() {
@@ -149,7 +159,9 @@
       allStaffList: [],
       filteredStaffList: [],
       staffSearchKeyword: '',
-      staffFilterType: 'driver' // 榛樿閫変腑鍙告満
+      staffFilterType: 'driver', // 榛樿閫変腑鍙告満
+      staffListCache: {}, // 缂撳瓨: { key: { data: [], timestamp: 0 } }
+      cacheExpireTime: 5 * 60 * 1000 // 缂撳瓨杩囨湡鏃堕棿锛�5鍒嗛挓
     }
   },
   computed: {
@@ -174,6 +186,23 @@
       },
       immediate: true,
       deep: true
+    },
+    // 鐩戝惉鍒嗗叕鍙窱D鏁扮粍鍙樺寲锛岄噸鏂板姞杞界敤鎴峰垪琛�
+    branchDeptIds: {
+      handler(newVal, oldVal) {
+        if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
+          console.log('鍒嗗叕鍙窱D鍙樺寲锛岄噸鏂板姞杞界敤鎴�:', newVal)
+          this.loadStaffList()
+        }
+      },
+      deep: true
+    },
+    // 鐩戝惉鍗曚釜鍒嗗叕鍙窱D鍙樺寲
+    branchDeptId(newVal, oldVal) {
+      if (newVal !== oldVal) {
+        console.log('鍒嗗叕鍙窱D鍙樺寲锛岄噸鏂板姞杞界敤鎴�:', newVal)
+        this.loadStaffList()
+      }
     }
   },
   mounted() {
@@ -207,30 +236,74 @@
     
     // 鍔犺浇浜哄憳鍒楄〃
     loadStaffList() {
-      listBranchUsers().then(response => {
+      // 鑾峰彇鎵�鏈夐儴闂↖D
+      let deptIds = []
+      if (this.branchDeptIds && this.branchDeptIds.length > 0) {
+        deptIds = this.branchDeptIds
+      } else if (this.branchDeptId) {
+        deptIds = [this.branchDeptId]
+      }
+          
+      if (deptIds.length > 0) {
+        console.log('鏍规嵁鍒嗗叕鍙窱D鍔犺浇鐢ㄦ埛:', deptIds)
+        this.loadStaffByBranchDepts(deptIds)
+      } else {
+        console.log('鏈紶鍏ュ垎鍏徃ID,缁勪欢涓嶅姞杞戒汉鍛樺垪琛�')
+        this.$modal && this.$modal.showToast && this.$modal.showToast('璇蜂紶鍏ュ垎鍏徃ID')
+      }
+    },
+    
+    // 鏍规嵁鍒嗗叕鍙窱D鏁扮粍鍔犺浇鐢ㄦ埛锛堟敮鎸佺紦瀛橈級
+    loadStaffByBranchDepts(deptIds) {
+      // 鐢熸垚缂撳瓨key锛堟寜鎺掑簭鍚巌d鎷兼帴锛�
+      const cacheKey = [...deptIds].sort((a, b) => a - b).join(',')
+      const cached = this.staffListCache[cacheKey]
+      const now = Date.now()
+      
+      // 妫�鏌ョ紦瀛樻槸鍚︽湁鏁�
+      if (cached && (now - cached.timestamp) < this.cacheExpireTime) {
+        console.log('浣跨敤缂撳瓨鐨勪汉鍛樺垪琛�:', cacheKey)
+        this.processUserList(cached.data)
+        return
+      }
+      
+      // 缂撳瓨澶辨晥鎴栦笉瀛樺湪,璋冪敤鎺ュ彛
+      console.log('鍔犺浇浜哄憳鍒楄〃:', deptIds)
+      listUsersByBranchDepts(deptIds).then(response => {
         const userList = response.data || []
-        
-        this.allStaffList = userList.map(user => ({
-          userId: user.userId,
-          nickName: user.nickName,
-          phonenumber: user.phonenumber,
-          deptName: user.dept?.deptName || '',
-          postName: user.posts && user.posts.length > 0 ? user.posts[0].postName : '',
-          roleName: user.roles && user.roles.length > 0 ? user.roles[0].roleName : '',
-          posts: user.posts || [],
-          roles: user.roles || [],
-          dept: user.dept || null,
-          // 鏀寔澶氱绫诲瀷
-          types: this.getUserTypes(user),
-          type: this.getUserTypes(user)[0] || 'driver' // 涓昏绫诲瀷锛堢敤浜庡悜鍚庡吋瀹癸級
-        }))
-        
-        this.filterStaffList()
+        // 鏇存柊缂撳瓨
+        this.staffListCache[cacheKey] = {
+          data: userList,
+          timestamp: now
+        }
+        this.processUserList(userList)
       }).catch(error => {
         console.error('鍔犺浇浜哄憳鍒楄〃澶辫触:', error)
         this.$modal.showToast('鍔犺浇浜哄憳鍒楄〃澶辫触')
       })
     },
+
+    
+    
+    // 澶勭悊鐢ㄦ埛鍒楄〃鏁版嵁
+    processUserList(userList) {
+      this.allStaffList = userList.map(user => ({
+        userId: user.userId,
+        nickName: user.nickName,
+        phonenumber: user.phonenumber,
+        deptName: user.dept?.deptName || '',
+        postName: user.posts && user.posts.length > 0 ? user.posts[0].postName : '',
+        roleName: user.roles && user.roles.length > 0 ? user.roles[0].roleName : '',
+        posts: user.posts || [],
+        roles: user.roles || [],
+        dept: user.dept || null,
+        // 鏀寔澶氱绫诲瀷
+        types: this.getUserTypes(user),
+        type: this.getUserTypes(user)[0] || 'driver' // 涓昏绫诲瀷锛堢敤浜庡悜鍚庡吋瀹癸級
+      }))
+      
+      this.filterStaffList()
+    },
     
     // 鏍规嵁鐢ㄦ埛鐨勫矖浣嶆垨瑙掕壊鍒ゆ柇鎵�鏈夌被鍨嬶紙鏀寔澶氱韬唤锛�
     getUserTypes(user) {
diff --git a/app/pagesTask/create-emergency.vue b/app/pagesTask/create-emergency.vue
index 5f557c7..a752eda 100644
--- a/app/pagesTask/create-emergency.vue
+++ b/app/pagesTask/create-emergency.vue
@@ -23,6 +23,7 @@
       </view>
         <view class="form-item">
         <OrganizationSelector 
+          ref="organizationSelector"
           v-model="selectedOrganizationId"
           :required="true"
           :auto-select-user-dept="true"
@@ -65,6 +66,7 @@
         :required="false"
         :auto-add-current-user="true"
         :current-user-removable="false"
+        :branch-dept-ids="allOrganizationIds"
         @change="onStaffChange"
       />
       
@@ -266,7 +268,8 @@
     return {
       selectedVehicle: '',
       selectedVehicleId: null,
-      selectedOrganizationId: null, // 褰掑睘鏈烘瀯ID锛堥儴闂↖D锛�
+      selectedOrganizationId: null, // 褰撳墠閫変腑鐨勫綊灞炴満鏋処D
+      allOrganizationIds: [], // 鎵�鏈夊彲閫夋満鏋処D鏁扮粍
       selectedOrganizationServiceOrderClass: '', // 褰掑睘鏈烘瀯鐨勬湇鍔″崟缂栫爜
       selectedRegion: '', // 浠庡綊灞炴満鏋勪腑鎻愬彇鐨勫湴鍩熶俊鎭紙濡傦細骞垮窞銆佹繁鍦崇瓑锛�
       departureAddress: '', // 鍑哄彂鍦板湴鍧�
@@ -360,6 +363,8 @@
     this.loadEmergencyTaskTypes()
     // 鍔犺浇鍗曟嵁绫诲瀷鏁版嵁
     this.loadDocumentTypes()
+    // 鍔犺浇鎵�鏈夋満鏋処D
+    this.loadAllOrganizationIds()
   },
   methods: {
     // 鑾峰彇鐢ㄦ埛缁戝畾鐨勮溅杈嗕俊鎭�
@@ -446,6 +451,23 @@
 		return region.replace(/(鍒嗗叕鍙竱鎬诲叕鍙竱鎬婚儴)$/g, '').trim();
 	},
     
+    // 鍔犺浇鎵�鏈夋満鏋処D
+    loadAllOrganizationIds() {
+      // 閫氳繃 OrganizationSelector 缁勪欢鑾峰彇鎵�鏈夋満鏋�
+      const orgSelector = this.$refs.organizationSelector
+      if (orgSelector) {
+        orgSelector.reload().then(organizations => {
+          this.allOrganizationIds = organizations.map(org => org.deptId)
+          console.log('鎵�鏈夋満鏋処D:', this.allOrganizationIds)
+        })
+      } else {
+        // 濡傛灉缁勪欢杩樻湭鎸傝浇,绋嶅悗閲嶈瘯
+        setTimeout(() => {
+          this.loadAllOrganizationIds()
+        }, 100)
+      }
+    },
+    
     // 鍔犺浇绉戝鏁版嵁锛堜粠 SQL Server 鍔ㄦ�佸姞杞斤級
     loadDepartments() {
       getHospitalDepartments().then(response => {
diff --git a/app/pagesTask/edit-emergency.vue b/app/pagesTask/edit-emergency.vue
index 67c10f4..cb0d7dc 100644
--- a/app/pagesTask/edit-emergency.vue
+++ b/app/pagesTask/edit-emergency.vue
@@ -338,8 +338,10 @@
           this.taskForm.hospitalOut.id = info.hospitalOutId || null
           this.taskForm.hospitalOut.name = info.hospitalOutName || ''
           this.taskForm.hospitalOut.department = info.hospitalOutDepartment || ''
+          this.taskForm.hospitalOut.departmentId = info.hospitalOutDepartmentId || null
           this.taskForm.hospitalOut.bedNumber = info.hospitalOutBedNumber || ''
           this.taskForm.hospitalOut.address = info.hospitalOutAddress || ''
+          console.log('杞嚭鍖婚櫌绉戝ID:', info.hospitalOutDepartmentId)
           
           // 鍔犺浇杞嚭鍖婚櫌GPS鍧愭爣锛堜笉鏄剧ず锛屼絾淇濆瓨鍦ㄦ暟鎹腑锛�
           if (info.hospitalOutLongitude && info.hospitalOutLatitude) {
@@ -354,8 +356,10 @@
           this.taskForm.hospitalIn.id = info.hospitalInId || null
           this.taskForm.hospitalIn.name = info.hospitalInName || ''
           this.taskForm.hospitalIn.department = info.hospitalInDepartment || ''
+          this.taskForm.hospitalIn.departmentId = info.hospitalInDepartmentId || null
           this.taskForm.hospitalIn.bedNumber = info.hospitalInBedNumber || ''
           this.taskForm.hospitalIn.address = info.hospitalInAddress || ''
+          console.log('杞叆鍖婚櫌绉戝ID:', info.hospitalInDepartmentId)
           
           // 鍔犺浇杞叆鍖婚櫌GPS鍧愭爣锛堜笉鏄剧ず锛屼絾淇濆瓨鍦ㄦ暟鎹腑锛�
           if (info.hospitalInLongitude && info.hospitalInLatitude) {
@@ -854,6 +858,9 @@
         this.loading = true
         const submitData = this.buildSubmitData()
         
+        console.log('鎻愪氦鏁版嵁 - 杞嚭鍖婚櫌绉戝ID:', submitData.hospitalOut.departmentId)
+        console.log('鎻愪氦鏁版嵁 - 杞叆鍖婚櫌绉戝ID:', submitData.hospitalIn.departmentId)
+        
         updateTask(submitData).then(response => {
           this.loading = false
           console.log('浠诲姟鏇存柊鍝嶅簲:', response)
diff --git a/dryad-payment/src/main/java/com/ruoyi/payment/infrastructure/channel/alipay/AlipayThirdPartyClient.java b/dryad-payment/src/main/java/com/ruoyi/payment/infrastructure/channel/alipay/AlipayThirdPartyClient.java
index 81c1363..39b5fcb 100644
--- a/dryad-payment/src/main/java/com/ruoyi/payment/infrastructure/channel/alipay/AlipayThirdPartyClient.java
+++ b/dryad-payment/src/main/java/com/ruoyi/payment/infrastructure/channel/alipay/AlipayThirdPartyClient.java
@@ -1,5 +1,6 @@
 package com.ruoyi.payment.infrastructure.channel.alipay;
 
+import com.ruoyi.payment.infrastructure.config.AlipayConfig;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.http.HttpEntity;
 import org.apache.http.client.methods.CloseableHttpResponse;
@@ -8,7 +9,10 @@
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
 
 /**
  * 鏀粯瀹濈涓夋柟鎺ュ彛瀹㈡埛绔�
@@ -20,10 +24,12 @@
 @Component
 public class AlipayThirdPartyClient {
 
+    @Autowired
+    private AlipayConfig alipayConfig;
     /**
      * 绗笁鏂规敮浠樺疂褰撻潰浠樻帴鍙e湴鍧�
      */
-    private static final String THIRD_PARTY_ALIPAY_URL = "https://sys.966120.com.cn/alipay_pay_QR_NotifyUrl.php";
+//    private  String THIRD_PARTY_ALIPAY_URL = "https://sys.966120.com.cn/alipay_pay_QR_NotifyUrl.php";
 
     /**
      * 绗笁鏂规敮浠樺疂鏌ヨ鎺ュ彛鍦板潃
@@ -46,22 +52,22 @@
                 outTradeNo, totalFee, serviceOrdId);
 
         try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
-            HttpPost httpPost = new HttpPost(THIRD_PARTY_ALIPAY_URL);
+            HttpPost httpPost = new HttpPost(this.alipayConfig.getThirdParty().getUrl());
             
             // 璁剧疆Cookie澶�
             httpPost.setHeader("Cookie", "CAMEName=");
-            
+            BigDecimal totalFeeYuan = BigDecimal.valueOf(totalFee / 100f);
             // 鏋勫缓multipart/form-data璇锋眰浣�
             HttpEntity entity = MultipartEntityBuilder.create()
                     .addTextBody("notify_url", notifyUrl)
                     .addTextBody("out_trade_no", outTradeNo)
-                    .addTextBody("total_fee", String.valueOf(totalFee))
+                    .addTextBody("total_fee", String.valueOf(totalFeeYuan))
                     .addTextBody("ServiceOrdID", serviceOrdId)
                     .build();
             
             httpPost.setEntity(entity);
             
-            log.info("鍙戦�佽姹傚埌绗笁鏂规帴鍙�: {}", THIRD_PARTY_ALIPAY_URL);
+            log.info("鍙戦�佽姹傚埌绗笁鏂规帴鍙�: {}", this.alipayConfig.getThirdParty().getUrl());
             
             // 鍙戦�佽姹�
             try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
diff --git "a/prd/StaffSelector\347\273\204\344\273\266\345\210\206\345\205\254\345\217\270\347\224\250\346\210\267\345\212\240\350\275\275\350\257\264\346\230\216.md" "b/prd/StaffSelector\347\273\204\344\273\266\345\210\206\345\205\254\345\217\270\347\224\250\346\210\267\345\212\240\350\275\275\350\257\264\346\230\216.md"
new file mode 100644
index 0000000..22c26a6
--- /dev/null
+++ "b/prd/StaffSelector\347\273\204\344\273\266\345\210\206\345\205\254\345\217\270\347\224\250\346\210\267\345\212\240\350\275\275\350\257\264\346\230\216.md"
@@ -0,0 +1,485 @@
+# StaffSelector 缁勪欢鍒嗗叕鍙哥敤鎴峰姞杞藉姛鑳借鏄�
+
+## 馃搵 鍔熻兘姒傝堪
+
+浼樺寲 `StaffSelector` 缁勪欢,鏀寔閫氳繃澶栭儴浼犲叆鍒嗗叕鍙窱D鍒楄〃鏉ュ姞杞芥寚瀹氬垎鍏徃鐨勭敤鎴�,鎻愪緵鏇寸伒娲荤殑浜哄憳閫夋嫨鍔熻兘銆�
+
+---
+
+## 馃攧 淇敼鍐呭
+
+### 1. 鍚庣鎺ュ彛鏂板
+
+#### **鏂囦欢**: `SysUserController.java`
+
+**鏂板鎺ュ彛**: `GET /system/user/branch/users/by-dept-ids`
+
+```java
+/**
+ * 鏍规嵁鍒嗗叕鍙窱D鍒楄〃鑾峰彇鐢ㄦ埛(灏忕▼搴忕涓撶敤)
+ * 鏀寔澶栭儴浼犲叆鍒嗗叕鍙窱D鏁扮粍,鏌ヨ杩欎簺鍒嗗叕鍙稿強鍏舵墍鏈夊瓙閮ㄩ棬鐨勭敤鎴�
+ */
+@GetMapping("/branch/users/by-dept-ids")
+public AjaxResult listUsersByBranchDeptIds(Long[] branchDeptIds)
+```
+
+**鍙傛暟**:
+- `branchDeptIds`: 鍒嗗叕鍙窱D鏁扮粍,渚嬪 `[101, 102]`
+
+**杩斿洖鏁版嵁**:
+```json
+{
+  "code": 200,
+  "msg": "鎿嶄綔鎴愬姛",
+  "data": [
+    {
+      "userId": 1,
+      "nickName": "寮犱笁",
+      "phonenumber": "13800138000",
+      "dept": {
+        "deptId": 201,
+        "deptName": "骞垮窞鍒嗗叕鍙�-杞﹂槦A"
+      },
+      "posts": [...],
+      "roles": [...]
+    }
+  ]
+}
+```
+
+---
+
+### 2. 鍓嶇API鎵╁睍
+
+#### **鏂囦欢**: `app/api/system/user.js`
+
+**鏂板鏂规硶**:
+```javascript
+// 鏍规嵁鍒嗗叕鍙窱D鍒楄〃鏌ヨ鐢ㄦ埛(灏忕▼搴忕涓撶敤)
+export function listUsersByBranchDeptIds(branchDeptIds) {
+  return request({
+    url: '/system/user/branch/users/by-dept-ids',
+    method: 'get',
+    params: {
+      branchDeptIds: branchDeptIds
+    }
+  })
+}
+```
+
+---
+
+### 3. StaffSelector 缁勪欢鍗囩骇
+
+#### **鏂囦欢**: `app/pagesTask/components/StaffSelector.vue`
+
+**鏂板 Props**:
+```javascript
+props: {
+  // ... 鍏朵粬 props
+  
+  // 鍒嗗叕鍙窱D鍒楄〃(澶栭儴浼犲叆,鐢ㄤ簬鎸囧畾鍔犺浇鍝簺鍒嗗叕鍙哥殑鐢ㄦ埛)
+  branchDeptIds: {
+    type: Array,
+    default: null
+  }
+}
+```
+
+**鏍稿績閫昏緫**:
+```javascript
+loadStaffList() {
+  // 濡傛灉浼犲叆浜嗗垎鍏徃ID,浣跨敤鎸囧畾鐨勫垎鍏徃
+  if (this.branchDeptIds && this.branchDeptIds.length > 0) {
+    this.loadStaffByBranchDeptIds(this.branchDeptIds)
+  } else {
+    // 鍚﹀垯浣跨敤褰撳墠鐢ㄦ埛鐨勫垎鍏徃
+    this.loadStaffByCurrentUser()
+  }
+}
+```
+
+**鐩戝惉鍙樺寲**:
+```javascript
+watch: {
+  branchDeptIds: {
+    handler(newVal, oldVal) {
+      if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
+        this.loadStaffList()
+      }
+    },
+    deep: true
+  }
+}
+```
+
+---
+
+## 馃摉 浣跨敤鏂瑰紡
+
+### 鏂瑰紡涓�: 榛樿鍔犺浇(浣跨敤褰撳墠鐢ㄦ埛鐨勫垎鍏徃)
+
+```vue
+<template>
+  <staff-selector 
+    v-model="selectedStaff"
+    label="鎵ц浠诲姟浜哄憳"
+    :required="true"
+  />
+</template>
+
+<script>
+import StaffSelector from '@/components/StaffSelector.vue'
+
+export default {
+  components: { StaffSelector },
+  data() {
+    return {
+      selectedStaff: []
+    }
+  }
+}
+</script>
+```
+
+**琛屼负**:
+- 鑷姩璋冪敤 `/system/user/branch/users` 鎺ュ彛
+- 鍔犺浇褰撳墠鐧诲綍鐢ㄦ埛鎵�灞炲垎鍏徃鍙婂叾瀛愰儴闂ㄧ殑鎵�鏈夌敤鎴�
+
+---
+
+### 鏂瑰紡浜�: 鎸囧畾鍒嗗叕鍙窱D(澶栭儴浼犲叆)
+
+```vue
+<template>
+  <view>
+    <!-- 鍒嗗叕鍙搁�夋嫨鍣� -->
+    <view class="form-item">
+      <view class="form-label">閫夋嫨鍒嗗叕鍙�</view>
+      <picker 
+        mode="multiSelector" 
+        @change="onBranchChange"
+        :value="selectedBranchIndex"
+        :range="branchList"
+        range-key="deptName"
+      >
+        <view class="picker-value">
+          {{ selectedBranchNames || '璇烽�夋嫨鍒嗗叕鍙�' }}
+        </view>
+      </picker>
+    </view>
+    
+    <!-- 浜哄憳閫夋嫨鍣� -->
+    <staff-selector 
+      v-model="selectedStaff"
+      label="鎵ц浠诲姟浜哄憳"
+      :required="true"
+      :branch-dept-ids="selectedBranchIds"
+    />
+  </view>
+</template>
+
+<script>
+import StaffSelector from '@/components/StaffSelector.vue'
+import { listBranchCompany } from '@/api/system/dept'
+
+export default {
+  components: { StaffSelector },
+  data() {
+    return {
+      branchList: [],           // 鎵�鏈夊垎鍏徃鍒楄〃
+      selectedBranchIds: [],    // 閫変腑鐨勫垎鍏徃ID鏁扮粍
+      selectedBranchNames: '',  // 閫変腑鐨勫垎鍏徃鍚嶇О
+      selectedStaff: []         // 閫変腑鐨勪汉鍛�
+    }
+  },
+  mounted() {
+    this.loadBranchList()
+  },
+  methods: {
+    // 鍔犺浇鍒嗗叕鍙稿垪琛�
+    loadBranchList() {
+      listBranchCompany().then(response => {
+        this.branchList = response.data || []
+      })
+    },
+    
+    // 鍒嗗叕鍙搁�夋嫨鍙樺寲
+    onBranchChange(e) {
+      const indices = e.detail.value
+      const selected = indices.map(index => this.branchList[index])
+      
+      this.selectedBranchIds = selected.map(dept => dept.deptId)
+      this.selectedBranchNames = selected.map(dept => dept.deptName).join('銆�')
+      
+      console.log('閫変腑鐨勫垎鍏徃ID:', this.selectedBranchIds)
+    }
+  }
+}
+</script>
+```
+
+**琛屼负**:
+- 鐢ㄦ埛閫夋嫨鍒嗗叕鍙稿悗,`selectedBranchIds` 鏇存柊
+- `StaffSelector` 鐩戝惉鍒� `branchDeptIds` 鍙樺寲
+- 鑷姩璋冪敤 `/system/user/branch/users/by-dept-ids?branchDeptIds=101,102`
+- 鍔犺浇鎸囧畾鍒嗗叕鍙稿強鍏跺瓙閮ㄩ棬鐨勬墍鏈夌敤鎴�
+
+---
+
+### 鏂瑰紡涓�: 鍔ㄦ�佸垏鎹㈠垎鍏徃
+
+```vue
+<template>
+  <view>
+    <!-- Tab 鍒囨崲鍒嗗叕鍙� -->
+    <view class="tabs">
+      <view 
+        class="tab-item"
+        :class="{ active: currentBranchId === branch.deptId }"
+        v-for="branch in branchList"
+        :key="branch.deptId"
+        @click="switchBranch(branch)"
+      >
+        {{ branch.deptName }}
+      </view>
+    </view>
+    
+    <!-- 浜哄憳閫夋嫨鍣� -->
+    <staff-selector 
+      v-model="selectedStaff"
+      label="鎵ц浠诲姟浜哄憳"
+      :branch-dept-ids="[currentBranchId]"
+    />
+  </view>
+</template>
+
+<script>
+import StaffSelector from '@/components/StaffSelector.vue'
+import { listBranchCompany } from '@/api/system/dept'
+
+export default {
+  components: { StaffSelector },
+  data() {
+    return {
+      branchList: [],
+      currentBranchId: null,
+      selectedStaff: []
+    }
+  },
+  mounted() {
+    this.loadBranchList()
+  },
+  methods: {
+    loadBranchList() {
+      listBranchCompany().then(response => {
+        this.branchList = response.data || []
+        if (this.branchList.length > 0) {
+          // 榛樿閫変腑绗竴涓垎鍏徃
+          this.currentBranchId = this.branchList[0].deptId
+        }
+      })
+    },
+    
+    switchBranch(branch) {
+      this.currentBranchId = branch.deptId
+      // branchDeptIds 鍙樺寲浼氳嚜鍔ㄨЕ鍙� StaffSelector 閲嶆柊鍔犺浇
+    }
+  }
+}
+</script>
+```
+
+---
+
+## 馃攽 Props 璇存槑
+
+| Props | 绫诲瀷 | 榛樿鍊� | 璇存槑 |
+|-------|------|--------|------|
+| `value` | Array | `[]` | 宸查�夋嫨鐨勪汉鍛樺垪琛�(v-model) |
+| `label` | String | `'鎵ц浠诲姟浜哄憳'` | 鏍囩鏂囨湰 |
+| `required` | Boolean | `false` | 鏄惁蹇呭~ |
+| `autoAddCurrentUser` | Boolean | `true` | 鏄惁鑷姩娣诲姞褰撳墠鐢ㄦ埛 |
+| `currentUserRemovable` | Boolean | `false` | 褰撳墠鐢ㄦ埛鏄惁鍙Щ闄� |
+| **`branchDeptIds`** | Array | `null` | **鍒嗗叕鍙窱D鍒楄〃(鏂板)** |
+
+---
+
+## 馃搳 鏁版嵁娴佺▼
+
+### 榛樿妯″紡(涓嶄紶 branchDeptIds)
+
+```
+1. 缁勪欢 mounted
+   鈫�
+2. loadStaffList()
+   鈫�
+3. 鍒ゆ柇 branchDeptIds 涓虹┖
+   鈫�
+4. 璋冪敤 listBranchUsers()
+   鈫�
+5. 鍚庣鏍规嵁褰撳墠鐢ㄦ埛鐨� oaOrderClass 鎴� deptId 鏌ヨ鍒嗗叕鍙�
+   鈫�
+6. 杩斿洖璇ュ垎鍏徃鍙婂叾瀛愰儴闂ㄧ殑鎵�鏈夌敤鎴�
+   鈫�
+7. processUserList() 澶勭悊鏁版嵁
+   鈫�
+8. 灞曠ず鐢ㄦ埛鍒楄〃
+```
+
+### 鎸囧畾妯″紡(浼犲叆 branchDeptIds)
+
+```
+1. 缁勪欢 mounted
+   鈫�
+2. loadStaffList()
+   鈫�
+3. 鍒ゆ柇 branchDeptIds 鏈夊��
+   鈫�
+4. 璋冪敤 listUsersByBranchDeptIds(branchDeptIds)
+   鈫�
+5. 鍚庣鏍规嵁浼犲叆鐨勫垎鍏徃ID鍒楄〃鏌ヨ
+   鈫�
+6. 杩斿洖鎸囧畾鍒嗗叕鍙稿強鍏跺瓙閮ㄩ棬鐨勬墍鏈夌敤鎴�
+   鈫�
+7. processUserList() 澶勭悊鏁版嵁
+   鈫�
+8. 灞曠ず鐢ㄦ埛鍒楄〃
+```
+
+### 鍔ㄦ�佸垏鎹�
+
+```
+1. 鐖剁粍浠朵慨鏀� branchDeptIds
+   鈫�
+2. StaffSelector 鐩戝惉鍒板彉鍖�(watch)
+   鈫�
+3. 閲嶆柊鎵ц loadStaffList()
+   鈫�
+4. 鏍规嵁鏂扮殑 branchDeptIds 鍔犺浇鐢ㄦ埛
+   鈫�
+5. 鏇存柊鐢ㄦ埛鍒楄〃
+```
+
+---
+
+## 鈿� 鎬ц兘浼樺寲
+
+### 1. 閬垮厤閲嶅鍔犺浇
+```javascript
+watch: {
+  branchDeptIds: {
+    handler(newVal, oldVal) {
+      // 鍙湁鐪熸鍙樺寲鏃舵墠閲嶆柊鍔犺浇
+      if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
+        this.loadStaffList()
+      }
+    },
+    deep: true
+  }
+}
+```
+
+### 2. 鍓嶇缂撳瓨
+鍙互鍦ㄧ埗缁勪欢灞傞潰缂撳瓨鐢ㄦ埛鍒楄〃:
+```javascript
+data() {
+  return {
+    userCache: {}  // { '101': [...users], '102': [...users] }
+  }
+}
+```
+
+---
+
+## 鉁� 娴嬭瘯鐢ㄤ緥
+
+### 娴嬭瘯1: 榛樿鍔犺浇
+**杈撳叆**: 涓嶄紶 `branchDeptIds`  
+**棰勬湡**: 鍔犺浇褰撳墠鐢ㄦ埛鐨勫垎鍏徃鐢ㄦ埛  
+**楠岃瘉**: 妫�鏌ョ敤鎴峰垪琛ㄦ槸鍚︾鍚堥鏈�
+
+### 娴嬭瘯2: 鍗曚釜鍒嗗叕鍙�
+**杈撳叆**: `branchDeptIds: [101]`  
+**棰勬湡**: 鍙姞杞� ID=101 鐨勫垎鍏徃鍙婂叾瀛愰儴闂ㄧ敤鎴�  
+**楠岃瘉**: 妫�鏌ョ敤鎴烽儴闂ㄦ槸鍚﹂兘灞炰簬璇ュ垎鍏徃
+
+### 娴嬭瘯3: 澶氫釜鍒嗗叕鍙�
+**杈撳叆**: `branchDeptIds: [101, 102]`  
+**棰勬湡**: 鍔犺浇涓や釜鍒嗗叕鍙稿強鍏跺瓙閮ㄩ棬鐨勭敤鎴�  
+**楠岃瘉**: 妫�鏌ョ敤鎴烽儴闂ㄦ槸鍚﹂兘灞炰簬杩欎袱涓垎鍏徃
+
+### 娴嬭瘯4: 鍔ㄦ�佸垏鎹�
+**鎿嶄綔**: 鍏堜紶 `[101]`,鍐嶆敼涓� `[102]`  
+**棰勬湡**: 鐢ㄦ埛鍒楄〃瀹炴椂鏇存柊  
+**楠岃瘉**: 妫�鏌ヤ袱娆″姞杞界殑鐢ㄦ埛鏄惁涓嶅悓
+
+### 娴嬭瘯5: 绌烘暟缁�
+**杈撳叆**: `branchDeptIds: []`  
+**棰勬湡**: 杩斿洖绌哄垪琛�  
+**楠岃瘉**: 鐢ㄦ埛鍒楄〃涓虹┖
+
+---
+
+## 馃挕 浣跨敤寤鸿
+
+### 鍦烘櫙1: 璺ㄥ垎鍏徃鍗忎綔浠诲姟
+褰撲换鍔¢渶瑕佸涓垎鍏徃鐨勪汉鍛樺崗浣滄椂:
+```vue
+<staff-selector 
+  :branch-dept-ids="[101, 102, 103]"
+  label="璺ㄥ尯鍩熷崗浣滀汉鍛�"
+/>
+```
+
+### 鍦烘櫙2: 鍖哄煙绛涢��
+鎻愪緵鍖哄煙閫夋嫨鍣�,鐢ㄦ埛鑷�夊垎鍏徃:
+```vue
+<region-selector v-model="selectedRegions" />
+<staff-selector 
+  :branch-dept-ids="selectedRegions.map(r => r.deptId)"
+/>
+```
+
+### 鍦烘櫙3: 鏉冮檺鎺у埗
+鏍规嵁鐢ㄦ埛鏉冮檺鍔ㄦ�侀檺鍒跺彲閫夊垎鍏徃:
+```javascript
+computed: {
+  allowedBranchIds() {
+    // 鏍规嵁鐢ㄦ埛瑙掕壊杩斿洖鍏佽鐨勫垎鍏徃ID
+    return this.currentUser.allowedBranches.map(b => b.deptId)
+  }
+}
+```
+
+---
+
+## 馃搶 娉ㄦ剰浜嬮」
+
+1. **鏁扮粍浼犻��**: `branchDeptIds` 蹇呴』鏄暟缁勭被鍨�,鍗充娇鍙湁涓�涓狪D涔熻鐢ㄦ暟缁� `[101]`
+2. **鍔ㄦ�佹洿鏂�**: 淇敼 `branchDeptIds` 浼氳嚜鍔ㄨЕ鍙戦噸鏂板姞杞�,鏃犻渶鎵嬪姩璋冪敤
+3. **绌哄�煎鐞�**: 浼犲叆 `null` 鎴栦笉浼犳椂,浣跨敤榛樿鐨勫綋鍓嶇敤鎴峰垎鍏徃
+4. **鏉冮檺鏍¢獙**: 鍚庣浼氭牴鎹敤鎴锋潈闄愯繃婊ゆ暟鎹�,鍓嶇鏃犻渶棰濆澶勭悊
+
+---
+
+## 馃敆 鐩稿叧鏂囦欢
+
+### 鍚庣
+- `SysUserController.java` - 鐢ㄦ埛鎺у埗鍣�
+- `SysUserService.java` - 鐢ㄦ埛鏈嶅姟鎺ュ彛  
+- `SysUserServiceImpl.java` - 鐢ㄦ埛鏈嶅姟瀹炵幇
+- `SysUserMapper.xml` - SQL鏄犲皠鏂囦欢
+
+### 鍓嶇
+- `app/api/system/user.js` - 鐢ㄦ埛API
+- `app/pagesTask/components/StaffSelector.vue` - 浜哄憳閫夋嫨鍣ㄧ粍浠�
+- `app/api/system/dept.js` - 閮ㄩ棬API
+
+---
+
+## 馃摑 鐗堟湰鍘嗗彶
+
+| 鐗堟湰 | 鏃ユ湡 | 璇存槑 |
+|------|------|------|
+| 1.0 | 2025-12-04 | 鍒濆鐗堟湰,鏂板鎸夊垎鍏徃ID鍔犺浇鐢ㄦ埛鍔熻兘 |
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
index 3142bb6..84af926 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
@@ -31,6 +31,7 @@
 import com.ruoyi.system.service.ISysPostService;
 import com.ruoyi.system.service.ISysRoleService;
 import com.ruoyi.system.service.ISysUserService;
+import com.ruoyi.system.domain.vo.BranchUserQueryVO;
 
 /**
  * 鐢ㄦ埛淇℃伅
@@ -319,6 +320,24 @@
     }
     
     /**
+     * 鏍规嵁鍒嗗叕鍙窱D鍒楄〃鑾峰彇鐢ㄦ埛(POST鏂瑰紡)
+     */
+    @PostMapping("/branch/users")
+    public AjaxResult listUsersByBranchDepts(@RequestBody BranchUserQueryVO queryVO)
+    {
+        List<Long> branchDeptIds = queryVO.getBranchDeptIds();
+        
+        if (branchDeptIds == null || branchDeptIds.isEmpty()) {
+            return success(new java.util.ArrayList<>());
+        }
+        
+        // 鏌ヨ杩欎簺鍒嗗叕鍙稿強鍏舵墍鏈夊瓙閮ㄩ棬鐨勭敤鎴�
+        List<SysUser> users = userService.selectUsersByBranchDeptIds(branchDeptIds);
+        
+        return success(users);
+    }
+    
+    /**
      * 鏍规嵁oaUserId鏌ヨ鐢ㄦ埛淇℃伅
      */
     @GetMapping("/oa-user/{oaUserId}")
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
index f568f97..c2c48c6 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
@@ -104,6 +104,8 @@
     /** 寰俊鏄电О */
     private String wechatNickname;
 
+
+
     public SysUser()
     {
 
@@ -361,6 +363,8 @@
     {
         this.wechatNickname = wechatNickname;
     }
+    
+
 
     @Override
     public String toString() {
@@ -389,6 +393,7 @@
             .append("openId", getOpenId())
             .append("unionId", getUnionId())
             .append("wechatNickname", getWechatNickname())
+
             .toString();
     }
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/BranchUserQueryVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/BranchUserQueryVO.java
new file mode 100644
index 0000000..87489a3
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/BranchUserQueryVO.java
@@ -0,0 +1,32 @@
+package com.ruoyi.system.domain.vo;
+
+import java.util.List;
+
+/**
+ * 鍒嗗叕鍙哥敤鎴锋煡璇O
+ * 
+ * @author ruoyi
+ */
+public class BranchUserQueryVO
+{
+    /** 鍒嗗叕鍙窱D鍒楄〃 */
+    private List<Long> branchDeptIds;
+
+    public List<Long> getBranchDeptIds()
+    {
+        return branchDeptIds;
+    }
+
+    public void setBranchDeptIds(List<Long> branchDeptIds)
+    {
+        this.branchDeptIds = branchDeptIds;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "BranchUserQueryVO{" +
+                "branchDeptIds=" + branchDeptIds +
+                '}';
+    }
+}
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 466e111..a04871a 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
@@ -2037,12 +2037,7 @@
                     }
                 }
             }
-            if (createVO.getHospitalOut().getLongitude() != null) {
-                existingInfo.setHospitalOutLongitude(createVO.getHospitalOut().getLongitude());
-            }
-            if (createVO.getHospitalOut().getLatitude() != null) {
-                existingInfo.setHospitalOutLatitude(createVO.getHospitalOut().getLatitude());
-            }
+
         }
         
         // 鏇存柊杞叆鍖婚櫌淇℃伅
@@ -2082,12 +2077,7 @@
                     }
                 }
             }
-            if (createVO.getHospitalIn().getLongitude() != null) {
-                existingInfo.setHospitalInLongitude(createVO.getHospitalIn().getLongitude());
-            }
-            if (createVO.getHospitalIn().getLatitude() != null) {
-                existingInfo.setHospitalInLatitude(createVO.getHospitalIn().getLatitude());
-            }
+
         }
         
         // 鏇存柊璐圭敤淇℃伅
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 e61b9f6..f795378 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
@@ -6,7 +6,9 @@
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -395,26 +397,69 @@
                 }
             }
             
-            // 4. 鏌ヨ璇ユ棩鏈熺殑浠诲姟鏃堕棿鍖洪棿锛岃绠椾换鍔¢噷绋嬪拰闈炰换鍔¢噷绋�
-            List<TaskTimeInterval> taskIntervals = vehicleMileageStatsMapper.selectTaskTimeIntervals(vehicleId, dayStart, dayEnd);
-            
+            // 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) {
-                Date segStart = segment.getSegmentStartTime();
-                Date segEnd = segment.getSegmentEndTime();
-                BigDecimal segDistance = segment.getSegmentDistance() != null ? segment.getSegmentDistance() : BigDecimal.ZERO;
+                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()));
                 
-                // 璁$畻璇ュ垎娈典笌浠诲姟鏃舵鐨勯噸鍙犳瘮渚�
-                double taskRatio = calculateTaskOverlapRatio(segStart, segEnd, taskIntervals);
+                // 浣跨敤Set缁熻鍘婚噸鐨勪换鍔D鏁伴噺
+                Set<Long> uniqueTaskIds = new HashSet<>();
                 
-                // 鍒嗘憞閲岀▼
-                BigDecimal taskDist = segDistance.multiply(BigDecimal.valueOf(taskRatio));
-                BigDecimal nonTaskDist = segDistance.multiply(BigDecimal.valueOf(1 - taskRatio));
+                // 鐩存帴鎸塼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);
+                    }
+                }
                 
-                taskMileage = taskMileage.add(taskDist);
-                nonTaskMileage = nonTaskMileage.add(nonTaskDist);
+                // 璁剧疆鍘婚噸鍚庣殑浠诲姟鏁伴噺
+                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();
             }
             
             // 璁$畻浠诲姟閲岀▼鍗犳瘮
@@ -463,7 +508,7 @@
             stats.setNonTaskMileage(nonTaskMileage.setScale(2, RoundingMode.HALF_UP));
             stats.setTaskRatio(taskRatio);
             stats.setGpsPointCount(totalGpsPoints);
-            stats.setTaskCount(taskIntervals == null ? 0 : taskIntervals.size());
+            stats.setTaskCount(taskCount);
             stats.setSegmentCount(segments.size());
             stats.setDataSource("segment"); // 鏍囪鏁版嵁鏉ユ簮涓哄垎娈垫眹鎬�
             
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatLoginServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatLoginServiceImpl.java
index cfbed27..50457cc 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatLoginServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatLoginServiceImpl.java
@@ -11,6 +11,9 @@
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.http.HttpUtils;
+import com.ruoyi.system.domain.SysConfig;
+import com.ruoyi.system.mapper.SysConfigMapper;
+import com.ruoyi.system.service.ISysConfigService;
 import com.ruoyi.system.service.IWechatLoginService;
 import com.ruoyi.system.service.ISysUserService;
 
@@ -30,6 +33,12 @@
     @Autowired
     private ISysUserService userService;
     
+    @Autowired
+    private ISysConfigService configService;
+    
+    @Autowired
+    private SysConfigMapper configMapper;
+    
     /**
      * 寰俊API - code2Session
      */
@@ -40,10 +49,28 @@
      */
     private static final String GET_PHONE_NUMBER_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber";
     
+    private static final String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
+    
     /**
      * 寰俊API - 鑾峰彇access_token
      */
-    private static final String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
+    /**
+     * 鏍规嵁configKey鍐欏叆鎴栨洿鏂皊ys_config
+     */
+    private void upsertConfig(String key, String value) {
+        SysConfig exist = configMapper.checkConfigKeyUnique(key);
+        if (exist != null && exist.getConfigId() != null) {
+            exist.setConfigValue(value);
+            configMapper.updateConfig(exist);
+        } else {
+            SysConfig cfg = new SysConfig();
+            cfg.setConfigKey(key);
+            cfg.setConfigName(key);
+            cfg.setConfigValue(value);
+            cfg.setConfigType("Y");
+            configMapper.insertConfig(cfg);
+        }
+    }
     
     /**
      * 閫氳繃寰俊code鑾峰彇openid鍜宻ession_key
@@ -154,6 +181,9 @@
                 return result;
             }
             
+            // 浼扮畻杩囨湡鏃堕棿(寰俊access_token榛樿7200绉掓湁鏁�)
+            java.util.Date accessTokenExpiresAt = new java.util.Date(System.currentTimeMillis() + 7200L * 1000L);
+
             // 鏋勫缓璇锋眰URL
             String url = GET_PHONE_NUMBER_URL + "?access_token=" + accessToken;
             
@@ -186,6 +216,9 @@
             result.put("phoneNumber", phoneInfo.getString("phoneNumber"));
             result.put("purePhoneNumber", phoneInfo.getString("purePhoneNumber"));
             result.put("countryCode", phoneInfo.getString("countryCode"));
+            // 杩斿洖褰撳墠浣跨敤鐨刟ccess_token鍙婃湁鏁堟湡锛屼究浜庤皟鐢ㄦ柟瀛樺偍
+            result.put("accessToken", accessToken);
+            result.put("accessTokenExpiresAt", accessTokenExpiresAt);
             
             return result;
         }
@@ -260,7 +293,7 @@
                 return result;
             }
             
-            // 5. 鏇存柊鐢ㄦ埛鐨勫井淇′俊鎭�
+            // 5. 鏇存柊鐢ㄦ埛鐨勫井淇′俊鎭�(鍖呮嫭access_token涓庤繃鏈熸椂闂�)
             SysUser updateUser = new SysUser();
             updateUser.setUserId(user.getUserId());
             updateUser.setOpenId(openId);
@@ -268,6 +301,17 @@
             {
                 updateUser.setUnionId(unionId);
             }
+            // 淇濆瓨鏈璋冪敤浣跨敤鐨刟ccess_token鍙婅繃鏈熸椂闂村埌搴旂敤绾ч厤缃紙渚夸簬鍚庣画澶嶇敤锛�
+            Object at = phoneResult.get("accessToken");
+            Object atExp = phoneResult.get("accessTokenExpiresAt");
+            if (at != null) {
+                String appId = wechatConfig.getAppId();
+                String tokenKey = "weixin.access_token." + appId;
+                String expireKey = "weixin.access_token_expires." + appId;
+                upsertConfig(tokenKey, at.toString());
+                long expireTs = (atExp instanceof java.util.Date) ? ((java.util.Date) atExp).getTime() : (System.currentTimeMillis() + 7200L * 1000L);
+                upsertConfig(expireKey, String.valueOf(expireTs));
+            }
             userService.updateUser(updateUser);
             
             log.info("鐢ㄦ埛{}寰俊淇℃伅鏇存柊鎴愬姛", user.getUserName());
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatTaskNotifyServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatTaskNotifyServiceImpl.java
index 48833d3..6b91981 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatTaskNotifyServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatTaskNotifyServiceImpl.java
@@ -5,12 +5,17 @@
 import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.WechatUtils;
-import com.ruoyi.system.domain.SysTask;
-import com.ruoyi.system.domain.SysTaskEmergency;
+import com.ruoyi.system.domain.SysConfig;
+import com.ruoyi.system.mapper.SysConfigMapper;
 import com.ruoyi.system.mapper.SysTaskEmergencyMapper;
 import com.ruoyi.system.mapper.SysTaskMapper;
+import com.ruoyi.system.domain.SysTask;
+import com.ruoyi.system.domain.SysTaskEmergency;
 import com.ruoyi.system.mapper.SysUserMapper;
 import com.ruoyi.system.service.IWechatTaskNotifyService;
+import com.ruoyi.system.service.ISysConfigService;
+import com.ruoyi.system.mapper.SysConfigMapper;
+import com.ruoyi.system.domain.SysConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -44,6 +49,73 @@
     @Autowired
     private WechatConfig wechatConfig;
     
+    @Autowired
+    private ISysConfigService configService;
+    
+    @Autowired
+    private SysConfigMapper configMapper;
+    
+    
+    /**
+     * 鑾峰彇搴旂敤绾у井淇ccessToken锛堝甫缂撳瓨锛�
+     * 浼樺厛浠巗ys_config璇诲彇骞跺垽鏂湁鏁堟湡锛涜繃鏈熷垯閲嶆柊鑾峰彇骞跺啓鍥瀞ys_config
+     */
+    private String getAppAccessToken() {
+        try {
+            String appId = wechatConfig.getAppId();
+            String tokenKey = "weixin.access_token." + appId;
+            String expireKey = "weixin.access_token_expires." + appId;
+            
+            String cachedToken = configService.selectConfigByKey(tokenKey);
+            String cachedExpireStr = configService.selectConfigByKey(expireKey);
+            long now = System.currentTimeMillis();
+            long expireTs = 0L;
+            if (StringUtils.isNotEmpty(cachedExpireStr)) {
+                try {
+                    expireTs = Long.parseLong(cachedExpireStr);
+                } catch (NumberFormatException e) {
+                    expireTs = 0L;
+                }
+            }
+            
+            // 缂撳瓨鏈夋晥涓旀湭杩囨湡锛堥鐣�60绉掑畨鍏ㄨ竟鐣岋級
+            if (StringUtils.isNotEmpty(cachedToken) && expireTs > now + 60000L) {
+                return cachedToken;
+            }
+            
+            // 閲嶆柊鑾峰彇锛屽苟鍐欏叆sys_config
+            String newToken = WechatUtils.getAccessToken(wechatConfig.getAppId(), wechatConfig.getAppSecret());
+            if (StringUtils.isEmpty(newToken)) {
+                return null;
+            }
+            long newExpireTs = now + 7200L * 1000L; // 7200绉�
+            upsertConfig(tokenKey, newToken);
+            upsertConfig(expireKey, String.valueOf(newExpireTs));
+            return newToken;
+        } catch (Exception e) {
+            log.error("鑾峰彇搴旂敤绾у井淇ccessToken澶辫触", e);
+            return null;
+        }
+    }
+    
+    /**
+     * 鏍规嵁configKey鍐欏叆鎴栨洿鏂皊ys_config
+     */
+    private void upsertConfig(String key, String value) {
+        SysConfig exist = configMapper.checkConfigKeyUnique(key);
+        if (exist != null && exist.getConfigId() != null) {
+            exist.setConfigValue(value);
+            configMapper.updateConfig(exist);
+        } else {
+            SysConfig cfg = new SysConfig();
+            cfg.setConfigKey(key);
+            cfg.setConfigName(key);
+            cfg.setConfigValue(value);
+            cfg.setConfigType("Y"); // 鍐呯疆鍙傛暟
+            configMapper.insertConfig(cfg);
+        }
+    }
+    
     /**
      * 鍙戦�佷换鍔¢�氱煡娑堟伅缁欐寚瀹氱敤鎴峰垪琛�
      * 
@@ -69,8 +141,8 @@
         // 鏌ヨ鎬ユ晳淇℃伅
         SysTaskEmergency emergency = sysTaskEmergencyMapper.selectSysTaskEmergencyByTaskId(taskId);
         
-        // 鑾峰彇寰俊AccessToken
-        String accessToken = WechatUtils.getAccessToken(wechatConfig.getAppId(), wechatConfig.getAppSecret());
+        // 鑾峰彇寰俊AccessToken锛堣蛋搴旂敤绾х紦瀛橈級
+        String accessToken = getAppAccessToken();
         if (StringUtils.isEmpty(accessToken)) {
             log.error("鑾峰彇寰俊AccessToken澶辫触锛屾棤娉曞彂閫佷换鍔¢�氱煡");
             return 0;
diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
index ffc09a9..a1f910a 100644
--- a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -206,7 +206,6 @@
  			<if test="oaOrderClass != null">oa_order_class = #{oaOrderClass},</if>
  			<if test="openId != null and openId != ''">open_id = #{openId},</if>
  			<if test="unionId != null and unionId != ''">union_id = #{unionId},</if>
- 			<if test="wechatNickname != null and wechatNickname != ''">wechat_nickname = #{wechatNickname},</if>
  			<if test="loginIp != null and loginIp != ''">login_ip = #{loginIp},</if>
  			<if test="loginDate != null">login_date = #{loginDate},</if>
  			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
diff --git a/ruoyi-ui/src/api/system/mileageStats.js b/ruoyi-ui/src/api/system/mileageStats.js
index 62f0cb3..0ceb07e 100644
--- a/ruoyi-ui/src/api/system/mileageStats.js
+++ b/ruoyi-ui/src/api/system/mileageStats.js
@@ -65,3 +65,16 @@
     }
   })
 }
+
+// 鏌ヨ杞﹁締鎸囧畾鏃ユ湡鐨凣PS鍒嗘閲岀▼鏄庣粏
+export function getSegmentsByDateRange(vehicleId, startDate, endDate) {
+  return request({
+    url: '/system/gpsSegment/range',
+    method: 'get',
+    params: {
+      vehicleId: vehicleId,
+      startDate: startDate,
+      endDate: endDate
+    }
+  })
+}
diff --git a/ruoyi-ui/src/views/system/mileageStats/index.vue b/ruoyi-ui/src/views/system/mileageStats/index.vue
index 8a6738c..4468509 100644
--- a/ruoyi-ui/src/views/system/mileageStats/index.vue
+++ b/ruoyi-ui/src/views/system/mileageStats/index.vue
@@ -131,9 +131,7 @@
           </el-tag>
         </template>
       </el-table-column>
-      <el-table-column label="GPS鐐规暟" align="center" prop="gpsPointCount" width="90" />
-      <el-table-column label="浠诲姟鏁�" align="center" prop="taskCount" width="80" />
-      <el-table-column label="鍒嗘鏁�" align="center" prop="segmentCount" width="80" />
+      
       <el-table-column label="缁熻鏃堕棿" align="center" prop="createTime" width="160">
         <template slot-scope="scope">
           <span>{{ parseTime(scope.row.createTime) }}</span>
@@ -216,7 +214,7 @@
     </el-dialog>
 
     <!-- 璇︽儏瀵硅瘽妗� -->
-    <el-dialog title="閲岀▼缁熻璇︽儏" :visible.sync="detailOpen" width="600px" append-to-body>
+    <el-dialog title="閲岀▼缁熻璇︽儏" :visible.sync="detailOpen" width="900px" append-to-body>
       <el-descriptions :column="2" border>
         <el-descriptions-item label="杞︾墝鍙�">{{ detailData.vehicleNo }}</el-descriptions-item>
         <el-descriptions-item label="褰掑睘鍒嗗叕鍙�">{{ detailData.deptName || '-' }}</el-descriptions-item>
@@ -237,18 +235,58 @@
             {{ formatRatio(detailData.taskRatio) }}
           </el-tag>
         </el-descriptions-item>
-        <el-descriptions-item label="GPS鐐规暟">{{ detailData.gpsPointCount }}</el-descriptions-item>
-        <el-descriptions-item label="浠诲姟鏁�">{{ detailData.taskCount }}</el-descriptions-item>
-        <el-descriptions-item label="鍒嗘鏁�">{{ detailData.segmentCount }}</el-descriptions-item>
-        <el-descriptions-item label="鏁版嵁鏉ユ簮">
-          <el-tag :type="detailData.dataSource === 'segment' ? 'success' : 'info'" size="small">
-            {{ detailData.dataSource === 'segment' ? '浠庡垎娈垫眹鎬�' : '鐩存帴璁$畻' }}
-          </el-tag>
-        </el-descriptions-item>
         <el-descriptions-item label="缁熻鏃堕棿" :span="2">
           {{ parseTime(detailData.createTime) }}
         </el-descriptions-item>
       </el-descriptions>
+      
+      <!-- 鍒嗘鏄庣粏琛ㄦ牸 -->
+      <div v-if="segmentList.length > 0" style="margin-top: 20px;">
+        <el-divider content-position="left">
+          <i class="el-icon-tickets"></i> 閲岀▼鍒嗘鏄庣粏
+        </el-divider>
+        
+        <el-table 
+          :data="segmentList" 
+          size="small"
+          :max-height="400"
+          stripe
+          border
+          v-loading="segmentLoading"
+        >
+          <el-table-column type="index" label="搴忓彿" width="50" align="center" />
+          <el-table-column label="寮�濮嬫椂闂�" prop="segmentStartTime" width="160" align="center">
+            <template slot-scope="scope">
+              {{ parseTime(scope.row.segmentStartTime, '{y}-{m}-{d} {h}:{i}:{s}') }}
+            </template>
+          </el-table-column>
+          <el-table-column label="缁撴潫鏃堕棿" prop="segmentEndTime" width="160" align="center">
+            <template slot-scope="scope">
+              {{ parseTime(scope.row.segmentEndTime, '{y}-{m}-{d} {h}:{i}:{s}') }}
+            </template>
+          </el-table-column>
+          <el-table-column label="閲岀▼(km)" prop="segmentDistance" width="100" align="center">
+            <template slot-scope="scope">
+              <span class="mileage-value">{{ (scope.row.segmentDistance || 0).toFixed(2) }}</span>
+            </template>
+          </el-table-column>
+        
+          <el-table-column label="鍏宠仈浠诲姟" prop="taskCode" align="center" min-width="120">
+            <template slot-scope="scope">
+              <el-tag v-if="scope.row.taskCode" size="small" type="success">{{ scope.row.taskCode }}</el-tag>
+              <span v-else style="color: #909399;">鏃犱换鍔�</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="璁$畻鏂瑰紡" prop="calculateMethod" align="center" width="100">
+            <template slot-scope="scope">
+              <el-tag size="small" :type="scope.row.calculateMethod === 'haversine' ? 'primary' : 'info'">
+                {{ scope.row.calculateMethod === 'haversine' ? '鐞冮潰璺濈' : '鐩寸嚎璺濈' }}
+              </el-tag>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      
       <div slot="footer" class="dialog-footer">
         <el-button @click="detailOpen = false">鍏� 闂�</el-button>
       </div>
@@ -257,7 +295,7 @@
 </template>
 
 <script>
-import { listMileageStats, getMileageStats, delMileageStats, calculateMileageStats, batchCalculateMileageStats } from "@/api/system/mileageStats";
+import { listMileageStats, getMileageStats, delMileageStats, calculateMileageStats, batchCalculateMileageStats, getSegmentsByDateRange } from "@/api/system/mileageStats";
 import { listDept } from "@/api/system/dept";
 
 export default {
@@ -292,6 +330,10 @@
       detailOpen: false,
       // 璇︽儏鏁版嵁
       detailData: {},
+      // 鍒嗘鏄庣粏鍒楄〃
+      segmentList: [],
+      // 鍒嗘鏄庣粏鍔犺浇鐘舵��
+      segmentLoading: false,
       // 鎵嬪姩缁熻鍔犺浇鐘舵��
       calculateLoading: false,
       // 鎵归噺缁熻鍔犺浇鐘舵��
@@ -417,9 +459,44 @@
     /** 鏌ョ湅璇︽儏鎸夐挳鎿嶄綔 */
     handleView(row) {
       const statsId = row.statsId;
+      
+      // 閲嶇疆鍒嗘鏄庣粏
+      this.segmentList = [];
+      
+      // 鍔犺浇缁熻璇︽儏
       getMileageStats(statsId).then(response => {
         this.detailData = response.data;
         this.detailOpen = true;
+        
+        // 濡傛灉鏈夎溅杈咺D鍜岀粺璁℃棩鏈燂紝鍔犺浇鍒嗘鏄庣粏
+        if (this.detailData.vehicleId && this.detailData.statDate) {
+          this.loadSegmentDetails(this.detailData.vehicleId, this.detailData.statDate);
+        }
+      });
+    },
+    
+    /** 鍔犺浇鍒嗘鏄庣粏 */
+    loadSegmentDetails(vehicleId, statDate) {
+      this.segmentLoading = true;
+      
+      // 鏍煎紡鍖栨棩鏈燂細缁熻鏃ユ湡鐨勫紑濮嬪拰缁撴潫鏃堕棿
+      const startDate = this.parseTime(statDate, '{y}-{m}-{d}');
+      const endDate = this.parseTime(statDate, '{y}-{m}-{d}');
+      
+      // 鏌ヨ璇ユ棩鏈熺殑鍒嗘閲岀▼鏄庣粏
+      getSegmentsByDateRange(vehicleId, startDate, endDate).then(response => {
+        if (response.code === 200 && response.data) {
+          this.segmentList = response.data;
+          console.log('鍒嗘鏄庣粏鏁版嵁锛�', this.segmentList);
+        } else {
+          this.segmentList = [];
+        }
+      }).catch(error => {
+        console.error('鍔犺浇鍒嗘鏄庣粏澶辫触', error);
+        this.segmentList = [];
+        this.$modal.msgWarning('鍔犺浇鍒嗘鏄庣粏澶辫触锛屼絾涓嶅奖鍝嶇粺璁℃暟鎹煡鐪�');
+      }).finally(() => {
+        this.segmentLoading = false;
       });
     },
     /** 鍒犻櫎鎸夐挳鎿嶄綔 */
diff --git a/sql/add_wechat_token_fields.sql b/sql/add_wechat_token_fields.sql
new file mode 100644
index 0000000..5c3c7dc
--- /dev/null
+++ b/sql/add_wechat_token_fields.sql
@@ -0,0 +1,9 @@
+-- 寰俊鐧诲綍token瀛樺偍瀛楁
+-- 鍦╯ys_user琛ㄦ柊澧炲瓨鍌ㄥ井淇ccess_token涓庤繃鏈熸椂闂寸殑瀛楁
+
+ALTER TABLE `sys_user`
+  ADD COLUMN `wechat_access_token` VARCHAR(1024) NULL COMMENT '寰俊AccessToken锛堢綉椤垫巿鏉冩垨鏈�杩戣幏鍙栵級' AFTER `wechat_nickname`,
+  ADD COLUMN `wechat_token_expires_at` DATETIME NULL COMMENT '寰俊AccessToken杩囨湡鏃堕棿' AFTER `wechat_access_token`;
+
+-- 绱㈠紩鍙�夛紙濡傞渶鎸塼oken鏌ヨ鎴栨竻鐞嗭級
+-- CREATE INDEX `idx_wechat_token_expire` ON `sys_user` (`wechat_token_expires_at`);
diff --git a/sql/fix_segment_mileage_task_association.sql b/sql/fix_segment_mileage_task_association.sql
new file mode 100644
index 0000000..d3b5a21
--- /dev/null
+++ b/sql/fix_segment_mileage_task_association.sql
@@ -0,0 +1,134 @@
+-- ========================================
+-- 淇GPS鍒嗘閲岀▼鐨勪换鍔″叧鑱�
+-- 鐢ㄩ�旓細涓哄凡鏈夌殑鍒嗘閲岀▼鏁版嵁琛ュ厖 task_id 鍜� task_code
+-- 浼樺寲璇存槑锛氳ˉ鍏卼ask_id鍚庯紝缁熻璁$畻灏嗕娇鐢ㄤ紭鍖栨柟妗堬紙鐩存帴SQL鑱氬悎锛屾洿蹇級
+-- ========================================
+
+-- 1. 鏌ョ湅褰撳墠鏈叧鑱斾换鍔$殑鍒嗘鏁伴噺
+SELECT COUNT(*) as '鏈叧鑱斾换鍔$殑鍒嗘鏁�' 
+FROM tb_vehicle_gps_segment_mileage 
+WHERE task_id IS NULL;
+
+-- 2. 鏌ョ湅鏈夊灏戜换鍔″彲浠ヨ鍏宠仈
+SELECT COUNT(DISTINCT t.task_id) as '鍙叧鑱旂殑浠诲姟鏁�'
+FROM sys_task t
+INNER JOIN sys_task_vehicle tv ON t.task_id = tv.task_id
+WHERE t.del_flag = '0'
+  AND t.task_status NOT IN ('PENDING', 'CANCELLED');
+
+-- 3. 鏇存柊閫昏緫锛氭牴鎹溅杈咺D鍜屾椂闂撮噸鍙犲叧鑱斾换鍔�
+-- 娉ㄦ剰锛氳繖涓煡璇細姣旇緝鎱紝寤鸿鍒嗘壒鎵ц鎴栧湪闈為珮宄版湡鎵ц
+
+UPDATE tb_vehicle_gps_segment_mileage seg
+INNER JOIN (
+    SELECT 
+        seg2.segment_id,
+        t.task_id,
+        t.task_code
+    FROM tb_vehicle_gps_segment_mileage seg2
+    INNER JOIN sys_task_vehicle tv ON seg2.vehicle_id = tv.vehicle_id
+    INNER JOIN sys_task t ON tv.task_id = t.task_id
+    WHERE seg2.task_id IS NULL
+      AND t.del_flag = '0'
+      -- AND t.task_status NOT IN ('PENDING', 'CANCELLED')
+      -- 鏃堕棿閲嶅彔鏉′欢锛氬垎娈靛紑濮嬫椂闂� < 浠诲姟缁撴潫鏃堕棿 AND 鍒嗘缁撴潫鏃堕棿 > 浠诲姟寮�濮嬫椂闂�
+      AND seg2.segment_start_time < COALESCE(t.actual_end_time, t.planned_end_time, DATE_ADD(t.create_time, INTERVAL 24 HOUR))
+      AND seg2.segment_end_time > COALESCE(t.actual_start_time, t.planned_start_time, t.create_time)
+    GROUP BY seg2.segment_id, t.task_id, t.task_code
+) task_match ON seg.segment_id = task_match.segment_id
+SET 
+    seg.task_id = task_match.task_id,
+    seg.task_code = task_match.task_code;
+
+-- 4. 鏌ョ湅淇缁撴灉
+SELECT 
+    '宸插叧鑱斾换鍔�' as '绫诲瀷',
+    COUNT(*) as '鏁伴噺',
+    CONCAT(ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM tb_vehicle_gps_segment_mileage), 2), '%') as '鍗犳瘮'
+FROM tb_vehicle_gps_segment_mileage 
+WHERE task_id IS NOT NULL
+UNION ALL
+SELECT 
+    '鏈叧鑱斾换鍔�' as '绫诲瀷',
+    COUNT(*) as '鏁伴噺',
+    CONCAT(ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM tb_vehicle_gps_segment_mileage), 2), '%') as '鍗犳瘮'
+FROM tb_vehicle_gps_segment_mileage 
+WHERE task_id IS NULL;
+
+-- 5. 鏌ョ湅姣忎釜杞﹁締鐨勫叧鑱旀儏鍐�
+SELECT 
+    seg.vehicle_id,
+    seg.vehicle_no,
+    COUNT(*) as '鎬诲垎娈垫暟',
+    SUM(CASE WHEN seg.task_id IS NOT NULL THEN 1 ELSE 0 END) as '宸插叧鑱斿垎娈垫暟',
+    CONCAT(ROUND(SUM(CASE WHEN seg.task_id IS NOT NULL THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2), '%') as '鍏宠仈鐜�'
+FROM tb_vehicle_gps_segment_mileage seg
+GROUP BY seg.vehicle_id, seg.vehicle_no
+ORDER BY COUNT(*) DESC
+LIMIT 20;
+
+-- 6. 鍒嗘瀽鏈兘鍏宠仈鐨勫師鍥�
+-- 鏌ョ湅鏌愪釜鏈叧鑱斿垎娈电殑璇︾粏淇℃伅
+SELECT 
+    seg.segment_id,
+    seg.vehicle_id,
+    seg.vehicle_no,
+    seg.segment_start_time,
+    seg.segment_end_time,
+    '璇ユ椂娈佃杞﹁締鐨勪换鍔�' as '璇存槑',
+    t.task_id,
+    t.task_code,
+    t.task_status,
+    t.create_time,
+    COALESCE(t.actual_start_time, t.planned_start_time) as '浠诲姟寮�濮嬫椂闂�',
+    COALESCE(t.actual_end_time, t.planned_end_time) as '浠诲姟缁撴潫鏃堕棿'
+FROM tb_vehicle_gps_segment_mileage seg
+LEFT JOIN sys_task_vehicle tv ON seg.vehicle_id = tv.vehicle_id
+LEFT JOIN sys_task t ON tv.task_id = t.task_id
+    AND t.del_flag = '0'
+    AND seg.segment_start_time < COALESCE(t.actual_end_time, t.planned_end_time, DATE_ADD(t.create_time, INTERVAL 24 HOUR))
+    AND seg.segment_end_time > COALESCE(t.actual_start_time, t.planned_start_time, t.create_time)
+WHERE seg.task_id IS NULL
+ORDER BY seg.segment_start_time DESC
+LIMIT 10;
+
+-- ========================================
+-- 浣跨敤璇存槑锛�
+-- 1. 鍏堟墽琛屾煡璇㈣鍙ワ紙1銆�2锛夋煡鐪嬫暟鎹儏鍐�
+-- 2. 鍦ㄩ潪楂樺嘲鏈熸墽琛屾洿鏂拌鍙ワ紙3锛�
+-- 3. 鎵ц缁撴灉鏌ヨ锛�4銆�5銆�6锛夐獙璇佷慨澶嶆晥鏋�
+-- 4. 濡傛灉鏁版嵁閲忓ぇ锛屽缓璁坊鍔� LIMIT 鍒嗘壒鎵ц
+-- ========================================
+
+-- ========================================
+-- 棰勯槻鎺柦锛氱‘淇濆畾鏃朵换鍔℃甯歌繍琛�
+-- ========================================
+
+-- 7. 妫�鏌PS鍒嗘閲岀▼璁$畻瀹氭椂浠诲姟鐘舵��
+SELECT 
+    job_id,
+    job_name,
+    job_group,
+    invoke_target,
+    cron_expression,
+    status as '鐘舵��(0=姝e父,1=鏆傚仠)',
+    create_time,
+    update_time
+FROM sys_job
+WHERE job_name LIKE '%GPS%' OR job_name LIKE '%閲岀▼%'
+ORDER BY create_time DESC;
+
+-- 8. 濡傛灉瀹氭椂浠诲姟鏄殏鍋滅姸鎬侊紝鍚姩瀹�
+-- UPDATE sys_job SET status = '0' WHERE job_name = 'GPS鍒嗘閲岀▼瀹炴椂璁$畻';
+
+-- 9. 妫�鏌ユ渶杩戠殑GPS璁$畻璁板綍
+SELECT 
+    vehicle_id,
+    vehicle_no,
+    MAX(segment_end_time) as '鏈�鍚庤绠楁椂闂�',
+    COUNT(*) as '鍒嗘鏁�',
+    SUM(CASE WHEN task_id IS NOT NULL THEN 1 ELSE 0 END) as '鏈変换鍔″叧鑱旂殑鍒嗘鏁�'
+FROM tb_vehicle_gps_segment_mileage
+WHERE segment_end_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
+GROUP BY vehicle_id, vehicle_no
+ORDER BY MAX(segment_end_time) DESC;
diff --git a/sql/remove_wechat_token_fields.sql b/sql/remove_wechat_token_fields.sql
new file mode 100644
index 0000000..83349d2
--- /dev/null
+++ b/sql/remove_wechat_token_fields.sql
@@ -0,0 +1,8 @@
+-- 绉婚櫎鐢ㄦ埛琛ㄤ腑鐨勫井淇oken涓庢湁鏁堟湡瀛楁锛堟敼涓哄簲鐢ㄧ骇缁熶竴瀛樺偍锛�
+-- 鍥炴粴鎴栨竻鐞嗚剼鏈細鍒犻櫎 sys_user.wechat_access_token / sys_user.wechat_token_expires_at
+
+ALTER TABLE `sys_user`
+  DROP COLUMN `wechat_access_token`;
+
+ALTER TABLE `sys_user`
+  DROP COLUMN `wechat_token_expires_at`;
diff --git a/sql/test_mileage_stats_optimization.sql b/sql/test_mileage_stats_optimization.sql
new file mode 100644
index 0000000..d169520
--- /dev/null
+++ b/sql/test_mileage_stats_optimization.sql
@@ -0,0 +1,110 @@
+-- ========================================
+-- 娴嬭瘯閲岀▼缁熻浼樺寲鏁堟灉
+-- 鐢ㄩ�旓細瀵规瘮浼樺寲鍓嶅悗鐨勬�ц兘鍜屽噯纭��
+-- ========================================
+
+-- 1. 鏌ョ湅鏌愪釜杞﹁締鏌愬ぉ鐨勫垎娈垫暟鎹畉ask_id瑕嗙洊鐜�
+-- 鏇挎崲鍙傛暟锛欯vehicleId, @statDate
+SET @vehicleId = 1;  -- 鏇挎崲涓哄疄闄呰溅杈咺D
+SET @statDate = '2025-12-04';  -- 鏇挎崲涓哄疄闄呮棩鏈�
+
+SELECT 
+    '鍒嗘缁熻' as '绫诲瀷',
+    COUNT(*) as '鎬诲垎娈垫暟',
+    SUM(CASE WHEN task_id IS NOT NULL THEN 1 ELSE 0 END) as '鏈変换鍔D鐨勫垎娈垫暟',
+    CONCAT(ROUND(SUM(CASE WHEN task_id IS NOT NULL THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2), '%') as 'task_id瑕嗙洊鐜�'
+FROM tb_vehicle_gps_segment_mileage
+WHERE vehicle_id = @vehicleId
+  AND DATE(segment_start_time) = @statDate;
+
+-- 2. 瀵规瘮涓ょ璁$畻鏂瑰紡鐨勭粨鏋�
+-- 鏂瑰紡A锛氫紭鍖栨柟妗堬紙鐩存帴鎸塼ask_id鑱氬悎锛�
+SELECT 
+    '浼樺寲鏂规' as '璁$畻鏂瑰紡',
+    SUM(segment_distance) as '鎬婚噷绋�',
+    SUM(CASE WHEN task_id IS NOT NULL THEN segment_distance ELSE 0 END) as '浠诲姟閲岀▼',
+    SUM(CASE WHEN task_id IS NULL THEN segment_distance ELSE 0 END) as '闈炰换鍔¢噷绋�',
+    CONCAT(ROUND(SUM(CASE WHEN task_id IS NOT NULL THEN segment_distance ELSE 0 END) * 100.0 / 
+           NULLIF(SUM(segment_distance), 0), 2), '%') as '浠诲姟鍗犳瘮'
+FROM tb_vehicle_gps_segment_mileage
+WHERE vehicle_id = @vehicleId
+  AND DATE(segment_start_time) = @statDate;
+
+-- 鏂瑰紡B锛氬綋鍓嶇粺璁¤〃涓殑鏁版嵁锛堝彲鑳戒娇鐢ㄤ簡鏃堕棿閲嶅彔璁$畻锛�
+SELECT 
+    '缁熻琛ㄦ暟鎹�' as '璁$畻鏂瑰紡',
+    total_mileage as '鎬婚噷绋�',
+    task_mileage as '浠诲姟閲岀▼',
+    non_task_mileage as '闈炰换鍔¢噷绋�',
+    CONCAT(ROUND(task_ratio * 100, 2), '%') as '浠诲姟鍗犳瘮'
+FROM tb_vehicle_mileage_stats
+WHERE vehicle_id = @vehicleId
+  AND DATE(stat_date) = @statDate;
+
+-- 3. 鏌ョ湅浣跨敤浼樺寲鏂规鐨勮溅杈嗘暟閲忥紙task_id瑕嗙洊鐜� > 80%锛�
+SELECT 
+    DATE(segment_start_time) as '鏃ユ湡',
+    vehicle_id,
+    vehicle_no,
+    COUNT(*) as '鎬诲垎娈垫暟',
+    SUM(CASE WHEN task_id IS NOT NULL THEN 1 ELSE 0 END) as '鏈塼ask_id鍒嗘鏁�',
+    CONCAT(ROUND(SUM(CASE WHEN task_id IS NOT NULL THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2), '%') as '瑕嗙洊鐜�',
+    CASE 
+        WHEN SUM(CASE WHEN task_id IS NOT NULL THEN 1 ELSE 0 END) > COUNT(*) * 0.8 
+        THEN '鉁� 浣跨敤浼樺寲鏂规' 
+        ELSE '脳 浣跨敤闄嶇骇鏂规' 
+    END as '鏂规閫夋嫨'
+FROM tb_vehicle_gps_segment_mileage
+WHERE segment_start_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
+GROUP BY DATE(segment_start_time), vehicle_id, vehicle_no
+HAVING COUNT(*) > 10  -- 鍙粺璁″垎娈垫暟澶т簬10鐨�
+ORDER BY DATE(segment_start_time) DESC, vehicle_id
+LIMIT 20;
+
+-- 4. 缁熻鏁翠綋浼樺寲鏁堟灉
+SELECT 
+    '鏁翠綋缁熻' as '缁熻绫诲瀷',
+    COUNT(DISTINCT CONCAT(vehicle_id, '_', DATE(segment_start_time))) as '杞﹁締-鏃ユ湡缁勫悎鎬绘暟',
+    SUM(CASE WHEN task_coverage > 0.8 THEN 1 ELSE 0 END) as '鍙娇鐢ㄤ紭鍖栨柟妗堢殑鏁伴噺',
+    CONCAT(ROUND(SUM(CASE WHEN task_coverage > 0.8 THEN 1 ELSE 0 END) * 100.0 / 
+           COUNT(DISTINCT CONCAT(vehicle_id, '_', DATE(segment_start_time))), 2), '%') as '浼樺寲瑕嗙洊鐜�'
+FROM (
+    SELECT 
+        vehicle_id,
+        DATE(segment_start_time) as stat_date,
+        SUM(CASE WHEN task_id IS NOT NULL THEN 1 ELSE 0 END) * 1.0 / COUNT(*) as task_coverage
+    FROM tb_vehicle_gps_segment_mileage
+    WHERE segment_start_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
+    GROUP BY vehicle_id, DATE(segment_start_time)
+    HAVING COUNT(*) > 10
+) coverage_stats;
+
+-- 5. 鎬ц兘娴嬭瘯锛氭墽琛屾椂闂村姣�
+-- 璇存槑锛氫娇鐢‥XPLAIN ANALYZE鏌ョ湅鎵ц璁″垝锛圡ySQL 8.0+锛夋垨浣跨敤BENCHMARK鍑芥暟
+
+-- 浼樺寲鏂规鏌ヨ锛堢洿鎺ヨ仛鍚堬級
+EXPLAIN 
+SELECT 
+    vehicle_id,
+    SUM(CASE WHEN task_id IS NOT NULL THEN segment_distance ELSE 0 END) as task_mileage,
+    SUM(CASE WHEN task_id IS NULL THEN segment_distance ELSE 0 END) as non_task_mileage
+FROM tb_vehicle_gps_segment_mileage
+WHERE vehicle_id = @vehicleId
+  AND DATE(segment_start_time) = @statDate
+GROUP BY vehicle_id;
+
+-- 6. 寤鸿鐨勪紭鍖栨帾鏂�
+SELECT 
+    '浼樺寲寤鸿' as '绫诲瀷',
+    '琛ュ厖鍘嗗彶鏁版嵁鐨則ask_id' as '鎺柦1',
+    '纭繚瀹氭椂浠诲姟姝e父杩愯' as '鎺柦2',
+    '纭繚浠诲姟鐘舵�佹纭洿鏂�' as '鎺柦3',
+    '瀹氭湡妫�鏌ask_id瑕嗙洊鐜�' as '鎺柦4';
+
+-- ========================================
+-- 浣跨敤璇存槑锛�
+-- 1. 璁剧疆鍙傛暟锛氫慨鏀圭7-8琛岀殑@vehicleId鍜孈statDate
+-- 2. 鎵ц鏌ヨ1-4鏌ョ湅浼樺寲鏁堟灉
+-- 3. 濡傛灉瑕嗙洊鐜囦綆浜�80%锛屾墽琛宖ix_segment_mileage_task_association.sql淇
+-- 4. 閲嶆柊缁熻锛岃瀵熸�ц兘鎻愬崌
+-- ========================================

--
Gitblit v1.9.1