From 57e98ac3f59e9ca12d3fdbc6f89c9c0b1f86be4d Mon Sep 17 00:00:00 2001
From: wlzboy <66905212@qq.com>
Date: 星期四, 05 二月 2026 00:49:10 +0800
Subject: [PATCH] feat:增加发票申请

---
 app/pages/mine/invoice/index.vue                                                     |  406 ++++
 app/pages.json                                                                       |   10 
 app/pages/mine/invoice/apply.vue                                                     |  584 ++++++
 app/pages/mine/index.vue                                                             |   12 
 ruoyi-ui/src/api/task.js                                                             |    8 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/SysInvoiceSyncTask.java             |   26 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysInvoiceController.java  |  234 ++
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/SysInvoiceTask.java                 |   33 
 ruoyi-system/src/main/java/com/ruoyi/system/domain/SysTaskEmergency.java             |    8 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/LegacyInvoiceMapper.java          |   37 
 ruoyi-ui/src/api/system/dept.js                                                      |    8 
 sql/invoice_menu.sql                                                                 |   27 
 sql/invoice_sync_from_legacy.sql                                                     |  124 +
 ruoyi-ui/src/router/index.js                                                         |   21 
 ruoyi-system/src/main/java/com/ruoyi/system/domain/SysInvoice.java                   |  380 ++++
 ruoyi-ui/src/api/system/invoice.js                                                   |   60 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysInvoiceServiceImpl.java  |  243 ++
 ruoyi-ui/src/views/system/invoice/audit.vue                                          |  311 +++
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskController.java       |   49 
 ruoyi-ui/src/views/task/general/detail.vue                                           |  109 +
 ruoyi-admin/src/main/resources/application.yml                                       |    2 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentSyncServiceImpl.java |    6 
 ruoyi-system/src/main/resources/mapper/system/LegacyInvoiceMapper.xml                |   34 
 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysInvoiceService.java          |   91 +
 app/api/invoice.js                                                                   |   36 
 ruoyi-system/src/main/resources/mapper/system/SysInvoiceMapper.xml                   |  302 +++
 ruoyi-ui/src/views/system/invoice/apply.vue                                          |  261 ++
 ruoyi-ui/src/views/task/general/index.vue                                            |   29 
 sql/invoice_sys.sql                                                                  |   32 
 ruoyi-ui/src/views/system/invoice/detail.vue                                         |  279 +++
 sql/分区优化方案说明.md                                                                      |  293 +++
 app/pagesTask/detail.vue                                                             |   82 
 sql/invoice_sync_job.sql                                                             |  248 ++
 ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml     |    2 
 ruoyi-ui/src/views/system/invoice/index.vue                                          |  265 ++
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysInvoiceMapper.java             |   91 +
 sql/partition_quick_setup.sql                                                        |  260 ++
 sql/partition_vehicle_gps_segment_mileage.sql                                        |  304 +++
 sql/InvoiceData.sql                                                                  |   31 
 39 files changed, 5,329 insertions(+), 9 deletions(-)

diff --git a/app/api/invoice.js b/app/api/invoice.js
new file mode 100644
index 0000000..410749e
--- /dev/null
+++ b/app/api/invoice.js
@@ -0,0 +1,36 @@
+import request from '@/utils/request'
+
+// 鏌ヨ鎴戠殑鍙戠エ鐢宠鍒楄〃
+export function listMyInvoice(query) {
+  return request({
+    url: '/system/invoice/myList',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鎻愪氦鍙戠エ鐢宠
+export function addInvoice(data) {
+  return request({
+    url: '/system/invoice',
+    method: 'post',
+    data: data
+  })
+}
+
+// 鑾峰彇鍙紑绁ㄧ殑浠诲姟鍒楄〃
+export function listSelectableTasks(params) {
+  return request({
+    url: '/system/invoice/selectableTasks',
+    method: 'get',
+    params: params
+  })
+}
+
+// 妫�鏌ヤ换鍔℃槸鍚﹀凡鐢宠鍙戠エ
+export function checkTaskInvoice(taskId) {
+  return request({
+    url: `/system/invoice/checkTaskInvoice/${taskId}`,
+    method: 'get'
+  })
+}
diff --git a/app/pages.json b/app/pages.json
index bb9fff8..e6b4e4e 100644
--- a/app/pages.json
+++ b/app/pages.json
@@ -110,6 +110,16 @@
     "style": {
       "navigationBarTitleText": "娑堟伅涓績"
     }
+  }, {
+    "path": "pages/mine/invoice/index",
+    "style": {
+      "navigationBarTitleText": "鎴戠殑鍙戠エ"
+    }
+  }, {
+    "path": "pages/mine/invoice/apply",
+    "style": {
+      "navigationBarTitleText": "鐢宠鍙戠エ"
+    }
   }],
   "subPackages": [{
     "root": "pagesTask",
diff --git a/app/pages/mine/index.vue b/app/pages/mine/index.vue
index e6ccd74..130f57e 100644
--- a/app/pages/mine/index.vue
+++ b/app/pages/mine/index.vue
@@ -58,6 +58,14 @@
             <view>{{ boundVehicle && boundVehicle !== '鏈粦瀹�' ? '鏇存崲杞﹁締' : '缁戝畾杞﹁締' }}</view>
           </view>
         </view>
+
+        <!-- 鎴戠殑鍙戠エ -->
+        <view class="list-cell list-cell-arrow" @click="handleToInvoice">
+          <view class="menu-item-box">
+            <view class="iconfont icon-edit menu-icon"></view>
+            <view>鎴戠殑鍙戠エ</view>
+          </view>
+        </view>
         
         <!-- 閫�鍑虹櫥褰� -->
         <view class="list-cell list-cell-arrow" @click="handleLogout">
@@ -144,6 +152,10 @@
         this.$tab.navigateTo('/pages/bind-vehicle')
       },
       
+      handleToInvoice() {
+        this.$tab.navigateTo('/pages/mine/invoice/index')
+      },
+      
       handleToInfo() {
         this.$tab.navigateTo('/pages/mine/info/index')
       },
diff --git a/app/pages/mine/invoice/apply.vue b/app/pages/mine/invoice/apply.vue
new file mode 100644
index 0000000..0c40475
--- /dev/null
+++ b/app/pages/mine/invoice/apply.vue
@@ -0,0 +1,584 @@
+<template>
+  <view class="invoice-apply-container bg-white">
+    <form>
+      <view class="cu-form-group margin-top">
+        <view class="title required">閫夋嫨浠诲姟</view>
+        <view class="combo-box-wrapper">
+          <input 
+            class="combo-input"
+            placeholder="璇疯緭鍏ユ湇鍔″崟鍙锋垨浠诲姟缂栧彿鎼滅储" 
+            v-model="searchKeyword" 
+            @input="handleInput"
+            @focus="handleFocus"
+            @blur="handleBlur"
+          />
+          <text class="cuIcon-search search-icon"></text>
+          
+          <!-- 涓嬫媺鍒楄〃 -->
+          <view class="dropdown-list" v-if="showDropdown && filteredTaskList.length > 0">
+            <view class="dropdown-item" 
+              v-for="task in filteredTaskList" 
+              :key="task.taskId"
+              @click="selectTask(task)"
+            >
+              <view class="item-main">
+                <text class="service-code">{{ task.serviceCode || task.taskCode }}</text>
+                <text class="task-code">{{ task.taskCode }}</text>
+              </view>
+              <view class="item-sub">
+                
+                <text class="text-gray margin-left-sm">{{ task.completionTime }}</text>
+              </view>
+            </view>
+          </view>
+          
+          <!-- 鏃犵粨鏋滄彁绀� -->
+          <view class="dropdown-list" v-if="showDropdown && searchKeyword && filteredTaskList.length === 0 && !loading">
+            <view class="empty-result">
+              <text class="text-gray">鏈壘鍒板尮閰嶇殑浠诲姟</text>
+            </view>
+          </view>
+          
+          <!-- 鍔犺浇鎻愮ず -->
+          <view class="dropdown-list" v-if="showDropdown && loading">
+            <view class="loading-result">
+              <text class="cuIcon-loading2 cu-spin"></text>
+              <text class="text-gray margin-left-sm">鎼滅储涓�...</text>
+            </view>
+          </view>
+        </view>
+      </view>
+      
+      <!-- 宸查�変换鍔℃樉绀� -->
+      <view class="selected-task" v-if="form.serviceOrderId">
+        <view class="label">宸查�変换鍔�</view>
+        <view class="task-card">
+          <view class="card-header">
+            <text class="service-code">{{ selectedTask.serviceCode || selectedTask.taskCode }}</text>
+            <text class="cuIcon-close remove-btn" @click="clearSelection"></text>
+          </view>
+          <view class="task-info-detail">
+            <view class="info-row">
+              <text class="info-label">鏈嶅姟鍗曞彿:</text>
+              <text class="info-value">{{ selectedTask.legacyServiceOrderId }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">鍑哄彂鍦�:</text>
+              <text class="info-value">{{ selectedTask.departure || '-' }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">鐩殑鍦�:</text>
+              <text class="info-value">{{ selectedTask.destination || '-' }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">瀹屾垚鏃堕棿:</text>
+              <text class="info-value">{{ selectedTask.completionTime }}</text>
+            </view>
+            <view class="info-row" v-if="selectedTask.transferPrice">
+              <text class="info-label">浠诲姟閲戦:</text>
+              <text class="info-value text-price">锟{ selectedTask.transferPrice }}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+      
+      <view class="cu-form-group">
+        <view class="title">寮�绁ㄧ被鍨�</view>
+        <radio-group class="flex" @change="handleTypeChange">
+          <view class="margin-right-sm">
+            <radio value="1" :checked="form.invoiceType == 1"></radio><text class="margin-left-xs">涓汉</text>
+          </view>
+          <view>
+            <radio value="2" :checked="form.invoiceType == 2"></radio><text class="margin-left-xs">浼佷笟</text>
+          </view>
+        </radio-group>
+      </view>
+      
+      <view class="cu-form-group">
+        <view class="title required">鍙戠エ鎶ご</view>
+        <input placeholder="璇疯緭鍏ュ彂绁ㄦ姮澶�" v-model="form.invoiceName" />
+      </view>
+      
+      <view class="cu-form-group">
+        <view class="title required">鍙戠エ閲戦</view>
+        <input 
+          type="digit" 
+          placeholder="璇疯緭鍏ラ噾棰�" 
+          v-model="form.invoiceMoney"
+          @blur="validateMoney"
+        />
+      </view>
+      <view class="money-hint" v-if="selectedTask && selectedTask.transferPrice">
+        <text class="text-gray text-sm">鏈�澶у彲寮�绁ㄩ噾棰�: 锟{ selectedTask.transferPrice }}</text>
+      </view>
+      
+      <view class="cu-form-group align-start">
+        <view class="title">鍙戠エ澶囨敞</view>
+        <textarea maxlength="-1" v-model="form.invoiceRemarks" placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�"></textarea>
+      </view>
+      
+      <block v-if="form.invoiceType == 2">
+        <view class="cu-form-group">
+          <view class="title">娉ㄥ唽鍦板潃</view>
+          <input placeholder="璇疯緭鍏ヤ紒涓氭敞鍐屽湴鍧�" v-model="form.companyAddress" />
+        </view>
+        <view class="cu-form-group">
+          <view class="title">寮�鎴烽摱琛�</view>
+          <input placeholder="璇疯緭鍏ヤ紒涓氬紑鎴烽摱琛�" v-model="form.companyBank" />
+        </view>
+        <view class="cu-form-group">
+          <view class="title">閾惰甯愬彿</view>
+          <input placeholder="璇疯緭鍏ヤ紒涓氶摱琛屽笎鍙�" v-model="form.companyBankNo" />
+        </view>
+      </block>
+      
+      <view class="cu-form-group margin-top-sm">
+        <view class="title">閭紪</view>
+        <input placeholder="璇疯緭鍏ラ偖缂�" v-model="form.zipCode" />
+      </view>
+      
+      <view class="cu-form-group">
+        <view class="title">閭瘎鍦板潃</view>
+        <input placeholder="璇疯緭鍏ラ偖瀵勫湴鍧�" v-model="form.mailAddress" />
+      </view>
+      
+      <view class="cu-form-group">
+        <view class="title">鑱旂郴浜�</view>
+        <input placeholder="璇疯緭鍏ヨ仈绯讳汉" v-model="form.contactName" />
+      </view>
+      
+      <view class="cu-form-group">
+        <view class="title">鑱旂郴鐢佃瘽</view>
+        <input placeholder="璇疯緭鍏ヨ仈绯荤數璇�" v-model="form.contactPhone" />
+      </view>
+      
+      <view class="padding flex flex-direction">
+        <button class="cu-btn bg-blue lg" @click="submit">鎻愪氦鐢宠</button>
+      </view>
+    </form>
+  </view>
+</template>
+
+<script>
+import { addInvoice, listSelectableTasks } from "@/api/invoice"
+
+export default {
+  data() {
+    return {
+      searchKeyword: '',
+      filteredTaskList: [],
+      selectedTask: null,
+      showDropdown: false,
+      loading: false,
+      searchTimer: null,
+      maxInvoiceMoney: null, // 鏈�澶у彲寮�绁ㄩ噾棰�
+      form: {
+        serviceOrderId: null,
+        legacyServiceOrderId: null,
+        invoiceType: 1,
+        invoiceName: '',
+        invoiceMoney: '',
+        invoiceRemarks: '',
+        companyAddress: '',
+        companyBank: '',
+        companyBankNo: '',
+        zipCode: '',
+        mailAddress: '',
+        contactName: '',
+        contactPhone: ''
+      },
+      // 浠庝换鍔¤鎯呴〉浼犲叆鐨勪换鍔′俊鎭�
+      taskInfoFromDetail: null
+    }
+  },
+  onLoad(options) {
+    // 妫�鏌ユ槸鍚︿粠浠诲姟璇︽儏椤典紶鍏ヤ簡浠诲姟淇℃伅
+    if (options && options.taskInfo) {
+      try {
+        const taskInfo = JSON.parse(decodeURIComponent(options.taskInfo));
+        this.taskInfoFromDetail = taskInfo;
+        this.preFillTaskInfo();
+      } catch (e) {
+        console.error('瑙f瀽浠诲姟淇℃伅澶辫触:', e);
+      }
+    }
+    // 涓嶅啀鑷姩鍔犺浇浠诲姟鍒楄〃
+  },
+  methods: {
+    // 棰勫~鍏呬换鍔′俊鎭�
+    preFillTaskInfo() {
+      if (!this.taskInfoFromDetail) return;
+            
+      const taskInfo = this.taskInfoFromDetail;
+            
+      // 濉厖琛ㄥ崟
+      this.form.serviceOrderId = taskInfo.taskId;
+      // 鍚屾椂璁剧疆鏃х郴缁熸湇鍔″崟ID
+      if (taskInfo.legacyServiceOrderId) {
+        this.form.legacyServiceOrderId = taskInfo.legacyServiceOrderId;
+      }
+      this.selectedTask = { ...taskInfo };
+            
+      // 璁剧疆鏈�澶у彲寮�绁ㄩ噾棰�
+      if (taskInfo.transferPrice) {
+        this.maxInvoiceMoney = parseFloat(taskInfo.transferPrice);
+        // 鑷姩濉叆浠诲姟閲戦
+        this.form.invoiceMoney = this.maxInvoiceMoney.toString();
+      }
+            
+      console.log('浠诲姟淇℃伅宸查濉厖:', taskInfo);
+    },
+    
+    handleInput(e) {
+      const keyword = e.detail.value.trim()
+      
+      // 娓呴櫎涔嬪墠鐨勫畾鏃跺櫒
+      if (this.searchTimer) {
+        clearTimeout(this.searchTimer)
+      }
+      
+      if (!keyword) {
+        this.showDropdown = false
+        this.filteredTaskList = []
+        return
+      }
+      
+      // 闃叉姈锛氬欢杩�300ms鎼滅储
+      this.searchTimer = setTimeout(() => {
+        this.searchTasks(keyword)
+      }, 300)
+    },
+    
+    handleFocus() {
+      // 濡傛灉鏈夎緭鍏ュ唴瀹癸紝鏄剧ず涓嬫媺鍒楄〃
+      if (this.searchKeyword && this.searchKeyword.trim()) {
+        this.showDropdown = true
+      }
+    },
+    
+    handleBlur() {
+      // 寤惰繜鍏抽棴涓嬫媺鍒楄〃锛岀‘淇濈偣鍑讳簨浠惰兘瑙﹀彂
+      setTimeout(() => {
+        this.showDropdown = false
+      }, 200)
+    },
+    
+    searchTasks(keyword) {
+      this.loading = true
+      this.showDropdown = true
+      
+      listSelectableTasks({
+        searchKeyword: keyword
+      }).then(res => {
+        this.filteredTaskList = this.formatTaskList(res.data)
+      }).catch(err => {
+        this.$modal.msgError('鎼滅储澶辫触锛岃閲嶈瘯')
+        this.filteredTaskList = []
+      }).finally(() => {
+        this.loading = false
+      })
+    },
+    formatTaskList(data) {
+      return data.map(item => {
+        // 瀹屾暣鏄剧ず yyyy-MM-dd HH:mm
+        const time = item.completionTime ? item.completionTime.substring(0, 16) : '';
+        // 鍏煎涓ょ瀛楁鍚嶏細transferPrice 鍜� transfer_price
+        const transferPrice = item.transferPrice !== undefined ? item.transferPrice : item.transfer_price;
+        return {
+          taskId: item.taskId,
+          taskCode: item.taskCode,
+          serviceCode: item.serviceCode,
+          legacyServiceOrderId: item.legacyServiceOrderId || item.taskCode,
+          completionTime: time,
+          departure: item.departure,
+          destination: item.destination,
+          transferPrice: transferPrice
+        }
+      })
+    },
+    handleSearch() {
+      if (!this.searchKeyword || this.searchKeyword.trim() === '') {
+        // 鏈緭鍏ユ椂鏄剧ず鎵�鏈�
+        this.getCompletedTasks()
+      } else {
+        // 璋冪敤鍚庣鎼滅储鎺ュ彛
+        listSelectableTasks({
+          searchKeyword: this.searchKeyword.trim()
+        }).then(res => {
+          this.filteredTaskList = this.formatTaskList(res.data)
+        })
+      }
+    },
+    selectTask(task) {
+      console.log('閫変腑鐨勪换鍔�:', task)
+      console.log('浠诲姟閲戦:', task.transferPrice)
+      
+      this.selectedTask = task
+      // serviceOrderId 瀛樺偍鏃х郴缁熸湇鍔″崟ID
+      this.form.serviceOrderId = task.taskId;
+      this.form.legacyServiceOrderId = task.legacyServiceOrderId
+      // 閫変腑鍚庢樉绀烘湇鍔″崟鍙凤紝鍏抽棴涓嬫媺
+      this.searchKeyword = task.serviceCode || task.taskCode
+      this.showDropdown = false
+      
+      // 鑷姩甯﹀叆浠诲姟閲戦鍒板彂绁ㄩ噾棰�
+      if (task.transferPrice !== null && task.transferPrice !== undefined) {
+        // 纭繚閲戦鏄暟瀛楃被鍨�
+        const price = Number(task.transferPrice)
+        if (!isNaN(price) && price > 0) {
+          this.form.invoiceMoney = price.toString()
+          this.maxInvoiceMoney = price
+          console.log('宸茶缃彂绁ㄩ噾棰�:', this.form.invoiceMoney)
+        } else {
+          console.log('浠诲姟閲戦鏃犳晥:', task.transferPrice)
+        }
+      } else {
+        console.log('浠诲姟閲戦涓虹┖')
+      }
+    },
+    
+    clearSelection() {
+      this.selectedTask = null
+      this.form.serviceOrderId = null
+      this.form.legacyServiceOrderId = null
+      this.form.invoiceMoney = ''
+      this.searchKeyword = ''
+      this.filteredTaskList = []
+      this.maxInvoiceMoney = null
+    },
+    
+    validateMoney() {
+      if (!this.form.invoiceMoney) return
+      
+      const money = parseFloat(this.form.invoiceMoney)
+      
+      if (isNaN(money) || money <= 0) {
+        this.$modal.msgError('璇疯緭鍏ユ湁鏁堢殑閲戦')
+        this.form.invoiceMoney = ''
+        return
+      }
+      
+      // 鏍¢獙鏄惁瓒呰繃浠诲姟閲戦
+      if (this.maxInvoiceMoney && money > this.maxInvoiceMoney) {
+        this.$modal.msgError(`鍙戠エ閲戦涓嶈兘瓒呰繃浠诲姟閲戦锟�${this.maxInvoiceMoney}`)
+        this.form.invoiceMoney = this.maxInvoiceMoney.toString()
+      }
+    },
+    handleTypeChange(e) {
+      this.form.invoiceType = e.detail.value
+    },
+    submit() {
+      if (!this.form.serviceOrderId) {
+        this.$modal.msgError("璇烽�夋嫨浠诲姟")
+        return
+      }
+      if (!this.form.invoiceName) {
+        this.$modal.msgError("璇疯緭鍏ュ彂绁ㄦ姮澶�")
+        return
+      }
+      if (!this.form.invoiceMoney) {
+        this.$modal.msgError("璇疯緭鍏ュ彂绁ㄩ噾棰�")
+        return
+      }
+      
+      // 鍐嶆鏍¢獙閲戦
+      const money = parseFloat(this.form.invoiceMoney)
+      if (isNaN(money) || money <= 0) {
+        this.$modal.msgError('璇疯緭鍏ユ湁鏁堢殑閲戦')
+        return
+      }
+      if (this.maxInvoiceMoney && money > this.maxInvoiceMoney) {
+        this.$modal.msgError(`鍙戠エ閲戦涓嶈兘瓒呰繃浠诲姟閲戦锟�${this.maxInvoiceMoney}`)
+        return
+      }
+      
+      addInvoice(this.form).then(res => {
+        this.$modal.msgSuccess("鎻愪氦鎴愬姛")
+        setTimeout(() => {
+          // 璺宠浆鍒板彂绁ㄥ垪琛ㄩ〉闈�
+          uni.redirectTo({
+            url: '/pages/mine/invoice/index'
+          })
+        }, 1500)
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.invoice-apply-container {
+  min-height: 100vh;
+  padding-bottom: 50rpx;
+  
+  .cu-form-group .title {
+    min-width: calc(4em + 15px);
+    
+    &.required::before {
+      content: '*';
+      color: #f56c6c;
+      margin-right: 4rpx;
+      font-weight: bold;
+    }
+  }
+  
+  // ComboBox鏍峰紡
+  .combo-box-wrapper {
+    position: relative;
+    flex: 1;
+    
+    .combo-input {
+      width: 100%;
+      padding-right: 60rpx;
+    }
+    
+    .search-icon {
+      position: absolute;
+      right: 20rpx;
+      top: 50%;
+      transform: translateY(-50%);
+      color: #8799a3;
+      font-size: 36rpx;
+    }
+    
+    .dropdown-list {
+      position: absolute;
+      top: 100%;
+      left: 0;
+      right: 0;
+      max-height: 500rpx;
+      overflow-y: auto;
+      background: white;
+      border: 2rpx solid #e1e1e1;
+      border-radius: 8rpx;
+      margin-top: 10rpx;
+      box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1);
+      z-index: 999;
+      
+      .dropdown-item {
+        padding: 20rpx;
+        border-bottom: 1rpx solid #f0f0f0;
+        transition: background 0.2s;
+        
+        &:active {
+          background: #f5f5f5;
+        }
+        
+        &:last-child {
+          border-bottom: none;
+        }
+        
+        .item-main {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          margin-bottom: 8rpx;
+          
+          .service-code {
+            font-size: 30rpx;
+            font-weight: bold;
+            color: #1976d2;
+          }
+          
+          .task-code {
+            font-size: 22rpx;
+            color: #999;
+          }
+        }
+        
+        .item-sub {
+          display: flex;
+          font-size: 24rpx;
+          color: #666;
+        }
+      }
+      
+      .empty-result, .loading-result {
+        padding: 40rpx 20rpx;
+        text-align: center;
+        color: #999;
+      }
+      
+      .loading-result {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+      }
+    }
+  }
+  
+  .selected-task {
+    margin: 20rpx 30rpx;
+    
+    .label {
+      font-size: 28rpx;
+      color: #666;
+      margin-bottom: 10rpx;
+    }
+    
+    .task-card {
+      padding: 20rpx;
+      background: #e8f5e9;
+      border-radius: 8rpx;
+      border: 2rpx solid #4caf50;
+      
+      .card-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 20rpx;
+        padding-bottom: 16rpx;
+        border-bottom: 1rpx solid #c8e6c9;
+      }
+      
+      .service-code {
+        font-size: 32rpx;
+        font-weight: bold;
+        color: #2e7d32;
+      }
+      
+      .remove-btn {
+        font-size: 40rpx;
+        color: #999;
+        padding: 10rpx;
+      }
+      
+      .task-info-detail {
+        .info-row {
+          display: flex;
+          margin-bottom: 12rpx;
+          line-height: 1.6;
+          
+          &:last-child {
+            margin-bottom: 0;
+          }
+          
+          .info-label {
+            font-size: 26rpx;
+            color: #666;
+            min-width: 140rpx;
+            flex-shrink: 0;
+          }
+          
+          .info-value {
+            font-size: 26rpx;
+            color: #333;
+            flex: 1;
+            word-break: break-all;
+            
+            &.text-price {
+              color: #f56c6c;
+              font-weight: bold;
+            }
+          }
+        }
+      }
+    }
+  }
+  
+  .money-hint {
+    margin: -10rpx 30rpx 20rpx 30rpx;
+    padding-left: calc(4em + 15px);
+  }
+}
+</style>
diff --git a/app/pages/mine/invoice/index.vue b/app/pages/mine/invoice/index.vue
new file mode 100644
index 0000000..d6b6018
--- /dev/null
+++ b/app/pages/mine/invoice/index.vue
@@ -0,0 +1,406 @@
+<template>
+  <view class="invoice-list-container">
+    <view class="add-btn-box">
+      <button class="cu-btn block bg-blue lg" @click="handleApply">鐢宠鍙戠エ</button>
+    </view>
+    
+    <!-- 鎼滅储妗� -->
+    <view class="search-box">
+      <view class="search-input-wrapper">
+        <input 
+          class="search-input" 
+          placeholder="璇疯緭鍏ユ湇鍔″崟鍙锋悳绱�" 
+          v-model="searchKeyword" 
+          @confirm="handleSearch"
+        />
+        <text class="cuIcon-search search-icon" @click="handleSearch"></text>
+        <text v-if="searchKeyword" class="cuIcon-close clear-icon" @click="clearSearch"></text>
+      </view>
+    </view>
+    
+    <!-- 鐘舵�乀ab -->
+    <view class="tabs-wrapper">
+      <view 
+        class="tab-item" 
+        :class="currentTab === null ? 'active' : ''"
+        @click="switchTab(null)"
+      >
+        <text>鍏ㄩ儴</text>
+      </view>
+      <view 
+        class="tab-item" 
+        :class="currentTab === 0 ? 'active' : ''"
+        @click="switchTab(0)"
+      >
+        <text>寰呭鏍�</text>
+      </view>
+      <view 
+        class="tab-item" 
+        :class="currentTab === 1 ? 'active' : ''"
+        @click="switchTab(1)"
+      >
+        <text>宸查�氳繃</text>
+      </view>
+      <view 
+        class="tab-item" 
+        :class="currentTab === 2 ? 'active' : ''"
+        @click="switchTab(2)"
+      >
+        <text>宸查┏鍥�</text>
+      </view>
+    </view>
+    
+    <scroll-view scroll-y="true" class="list-scroll" @scrolltolower="loadMore">
+      <!-- 绌哄垪琛ㄦ彁绀� -->
+      <view v-if="list.length === 0" class="empty-box">
+        <text class="text-gray">鏆傛棤鍙戠エ鐢宠璁板綍</text>
+      </view>
+      
+      <!-- 鍙戠エ鍒楄〃 -->
+      <view v-for="(item, index) in list" :key="index" class="invoice-item bg-white margin-sm padding-sm radius shadow">
+        <view class="flex justify-between border-bottom padding-bottom-xs margin-bottom-xs">
+          <text class="text-bold">鏈嶅姟鍗曞彿: {{ item.serviceCode || item.legacyServiceOrderId || '鏈煡' }}</text>
+          <text :class="item.status === 0 ? 'text-orange' : item.status === 1 ? 'text-green' : item.status === 2 ? 'text-red' : 'text-gray'">{{ item.status === 0 ? '寰呭鏍�' : item.status === 1 ? '宸查�氳繃' : item.status === 2 ? '宸查┏鍥�' : '鏈煡' }}</text>
+        </view>
+        <view class="info-row">
+          <text class="label">鍙戠エ鎶ご:</text>
+          <text class="value">{{ item.invoiceName }}</text>
+        </view>
+        <view class="info-row">
+          <text class="label">鐢宠閲戦:</text>
+          <text class="value text-red">锟{ item.invoiceMoney ? Number(item.invoiceMoney).toFixed(2) : '0.00' }}</text>
+        </view>
+        <view class="info-row">
+          <text class="label">鐢宠鏃堕棿:</text>
+          <text class="value">{{ formatApplyTime(item.applyTime) }}</text>
+        </view>
+        <view v-if="item.invoiceNo" class="info-row">
+          <text class="label">鍙戠エ缂栧彿:</text>
+          <text class="value">{{ item.invoiceNo }}</text>
+        </view>
+        
+        <view class="action-bar margin-top-sm flex justify-end" v-if="item.invoiceUrl">
+          <button class="cu-btn sm bg-green" @click="handleDownload" :data-url="item.invoiceUrl">鏌ョ湅鍙戠エ</button>
+        </view>
+        <view v-if="item.status === 2 && item.auditRemarks" class="margin-top-xs padding-xs bg-gray radius">
+          <text class="text-sm text-red">椹冲洖鍘熷洜: {{ item.auditRemarks }}</text>
+        </view>
+      </view>
+    </scroll-view>
+  </view>
+</template>
+
+<script>
+import { listMyInvoice } from "@/api/invoice"
+import config from '@/config.js'
+
+export default {
+  data() {
+    return {
+      list: [],
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        serviceCode: null,  // 鏈嶅姟鍗曞彿鎼滅储
+        status: null        // 鐘舵�佺瓫閫�
+      },
+      total: 0,
+      searchKeyword: '',    // 鎼滅储鍏抽敭璇�
+      currentTab: null      // 褰撳墠Tab锛歯ull=鍏ㄩ儴, 0=寰呭鏍�, 1=宸查�氳繃, 2=宸查┏鍥�
+    }
+  },
+  onShow() {
+    this.queryParams.pageNum = 1
+    this.list = []
+    this.getList()
+  },
+  methods: {
+    getList() {
+      listMyInvoice(this.queryParams).then(res => {
+        if (res.rows) {
+          let rows = res.rows;
+          // 鎸夌敵璇锋椂闂村�掑簭鎺掑簭
+          rows.sort((a, b) => {
+            // 濡傛灉鏈� applyTime锛屽垯姣旇緝鏃堕棿锛屽惁鍒欐斁鍦ㄦ渶鍚�
+            if (!a.applyTime) return 1;
+            if (!b.applyTime) return -1;
+            return new Date(b.applyTime) - new Date(a.applyTime);
+          });
+          this.list = this.list.concat(rows);
+          this.total = res.total || 0;
+        }
+      }).catch(err => {
+        console.error('鑾峰彇鍙戠エ鍒楄〃澶辫触:', err)
+        this.$modal.msgError('鍔犺浇澶辫触锛岃閲嶈瘯')
+      })
+    },
+    // 鎼滅储
+    handleSearch() {
+      this.queryParams.serviceCode = this.searchKeyword.trim() || null
+      this.resetList()
+    },
+    // 娓呯┖鎼滅储
+    clearSearch() {
+      this.searchKeyword = ''
+      this.queryParams.serviceCode = null
+      this.resetList()
+    },
+    // 鍒囨崲Tab
+    switchTab(status) {
+      this.currentTab = status
+      this.queryParams.status = status
+      this.resetList()
+    },
+    // 閲嶇疆鍒楄〃
+    resetList() {
+      this.queryParams.pageNum = 1
+      this.list = []
+      this.getList()
+    },
+    loadMore() {
+      if (this.list.length < this.total) {
+        this.queryParams.pageNum++
+        this.getList()
+      }
+    },
+    handleApply() {
+      this.$tab.navigateTo('/pages/mine/invoice/apply')
+    },
+    handleDownload(e) {
+      const url = e.currentTarget.dataset.url
+      console.log('=== 鍙戠エ涓嬭浇璋冭瘯淇℃伅 ===');
+      console.log('1. 鍘熷URL:', url)
+      
+      if (!url) {
+        this.$modal.msgError('鍙戠エ鏂囦欢鍦板潃涓虹┖')
+        return
+      }
+      
+      // 澶勭悊URL锛岀‘淇濇槸瀹屾暣鐨勫湴鍧�
+      let fullUrl = url
+      if (!url.startsWith('http')) {
+        // 濡傛灉鏄浉瀵硅矾寰勶紝鎷兼帴瀹屾暣鍦板潃
+        fullUrl = config.baseUrl + url
+      }
+      
+      console.log('2. baseUrl:', config.baseUrl)
+      console.log('3. 瀹屾暣URL:', fullUrl)
+      
+      const fileExt = fullUrl.match(/\.(\w+)$/)?.[1]?.toLowerCase()
+      console.log('4. 鏂囦欢鎵╁睍鍚�:', fileExt)
+      
+      // #ifdef H5
+      console.log('5. H5鐜锛岀洿鎺ユ墦寮�')
+      window.open(fullUrl)
+      // #endif
+      
+      // #ifndef H5
+      console.log('5. 灏忕▼搴�/App鐜')
+      
+      // 鍏堥瑙堝浘鐗囷紝濡傛灉鏄浘鐗囨牸寮�
+      const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']
+      if (imageExts.includes(fileExt)) {
+        console.log('6. 鍥剧墖鏂囦欢锛屼娇鐢╬reviewImage')
+        uni.previewImage({
+          urls: [fullUrl],
+          current: fullUrl,
+          success: () => {
+            console.log('鍥剧墖棰勮鎴愬姛')
+          },
+          fail: (err) => {
+            console.error('鍥剧墖棰勮澶辫触:', err)
+            this.$modal.msgError(`鏃犳硶棰勮鍥剧墖: ${err.errMsg || '鏈煡閿欒'}`)
+          }
+        })
+        return
+      }
+      
+      // PDF鏂囦欢涓嬭浇骞舵墦寮�
+      console.log('6. PDF鏂囦欢锛屼娇鐢╠ownloadFile')
+      uni.showLoading({ title: '涓嬭浇涓�...', mask: true })
+      
+      uni.downloadFile({
+        url: fullUrl,
+        success: (res) => {
+          console.log('7. 涓嬭浇鎴愬姛:', res)
+          uni.hideLoading()
+          
+          if (res.statusCode === 200) {
+            console.log('8. 鎵撳紑鏂囨。:', res.tempFilePath)
+            uni.openDocument({
+              filePath: res.tempFilePath,
+              showMenu: true,
+              success: function (openRes) {
+                console.log('9. 鏂囨。鎵撳紑鎴愬姛:', openRes)
+              },
+              fail: function (err) {
+                console.error('9. 鏂囨。鎵撳紑澶辫触:', err)
+                uni.showModal({
+                  title: '鎻愮ず',
+                  content: `鏃犳硶鎵撳紑鏂囦欢: ${err.errMsg || '鏈煡閿欒'}`,
+                  showCancel: false
+                })
+              }
+            })
+          } else {
+            console.error('8. 涓嬭浇澶辫触锛岀姸鎬佺爜:', res.statusCode)
+            this.$modal.msgError(`涓嬭浇澶辫触锛岀姸鎬佺爜: ${res.statusCode}`)
+          }
+        },
+        fail: (err) => {
+          console.error('7. 涓嬭浇澶辫触:', err)
+          uni.hideLoading()
+          uni.showModal({
+            title: '涓嬭浇澶辫触',
+            content: `閿欒淇℃伅: ${err.errMsg || '鏈煡閿欒'}\n\n璇锋鏌�:\n1. 缃戠粶杩炴帴鏄惁姝e父\n2. 鏂囦欢鏄惁瀛樺湪\n3. 鏈嶅姟鍣ㄦ槸鍚﹀彲璁块棶`,
+            showCancel: false
+          })
+        }
+      })
+      // #endif
+      console.log('=== 璋冭瘯淇℃伅缁撴潫 ===');
+    },
+    statusText(status) {
+      const map = { 0: '寰呭鏍�', 1: '宸查�氳繃', 2: '宸查┏鍥�' }
+      return map[status] || '鏈煡'
+    },
+    statusClass(status) {
+      const map = { 0: 'text-orange', 1: 'text-green', 2: 'text-red' }
+      return map[status] || 'text-gray'
+    },
+    formatMoney(money) {
+      if (money === null || money === undefined) return '0.00'
+      return Number(money).toFixed(2)
+    },
+    // 鏍煎紡鍖栫敵璇锋椂闂�
+    formatApplyTime(time) {
+      if (!time) return ''
+      // 灏嗘椂闂村瓧绗︿覆鏍煎紡鍖栦负 yyyy-MM-dd HH:mm
+      const date = new Date(time)
+      const year = date.getFullYear()
+      const month = String(date.getMonth() + 1).padStart(2, '0')
+      const day = String(date.getDate()).padStart(2, '0')
+      const hours = String(date.getHours()).padStart(2, '0')
+      const minutes = String(date.getMinutes()).padStart(2, '0')
+      return `${year}-${month}-${day} ${hours}:${minutes}`
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.invoice-list-container {
+  height: 100vh;
+  display: flex;
+  flex-direction: column;
+  background-color: #f8f8f8;
+  
+  .add-btn-box {
+    padding: 20rpx;
+    background-color: #fff;
+  }
+  
+  // 鎼滅储妗嗘牱寮�
+  .search-box {
+    padding: 20rpx;
+    background-color: #fff;
+    border-bottom: 1rpx solid #e5e5e5;
+    
+    .search-input-wrapper {
+      position: relative;
+      background: #f5f5f5;
+      border-radius: 40rpx;
+      padding: 0 80rpx 0 40rpx;
+      
+      .search-input {
+        height: 70rpx;
+        line-height: 70rpx;
+        font-size: 28rpx;
+      }
+      
+      .search-icon {
+        position: absolute;
+        right: 40rpx;
+        top: 50%;
+        transform: translateY(-50%);
+        color: #999;
+        font-size: 36rpx;
+      }
+      
+      .clear-icon {
+        position: absolute;
+        right: 80rpx;
+        top: 50%;
+        transform: translateY(-50%);
+        color: #999;
+        font-size: 32rpx;
+        padding: 10rpx;
+      }
+    }
+  }
+  
+  // Tab鏍峰紡
+  .tabs-wrapper {
+    display: flex;
+    background-color: #fff;
+    border-bottom: 1rpx solid #e5e5e5;
+    
+    .tab-item {
+      flex: 1;
+      text-align: center;
+      padding: 25rpx 0;
+      font-size: 28rpx;
+      color: #666;
+      position: relative;
+      
+      &.active {
+        color: #0081ff;
+        font-weight: bold;
+        
+        &::after {
+          content: '';
+          position: absolute;
+          bottom: 0;
+          left: 50%;
+          transform: translateX(-50%);
+          width: 60rpx;
+          height: 4rpx;
+          background-color: #0081ff;
+          border-radius: 2rpx;
+        }
+      }
+    }
+  }
+  
+  .list-scroll {
+    flex: 1;
+    overflow: hidden;
+  }
+  
+  .invoice-item {
+    .border-bottom {
+      border-bottom: 1rpx solid #eee;
+    }
+    
+    .info-row {
+      display: flex;
+      line-height: 1.8;
+      font-size: 26rpx;
+      
+      .label {
+        color: #666;
+        width: 140rpx;
+      }
+      .value {
+        color: #333;
+        flex: 1;
+      }
+    }
+  }
+  
+  .empty-box {
+    padding: 100rpx;
+    text-align: center;
+  }
+}
+</style>
diff --git a/app/pagesTask/detail.vue b/app/pagesTask/detail.vue
index 38a10c8..4bd3c08 100644
--- a/app/pagesTask/detail.vue
+++ b/app/pagesTask/detail.vue
@@ -237,7 +237,17 @@
       
       <!-- 杞繍 - 璐圭敤淇℃伅 -->
       <view class="detail-section" v-if="taskDetail.taskType === 'EMERGENCY_TRANSFER' && taskDetail.emergencyInfo">
-        <view class="section-title">璐圭敤淇℃伅</view>
+        <view class="section-title">
+          璐圭敤淇℃伅
+          <!-- 宸插畬鎴愪笖鏈敵璇峰彂绁ㄦ椂鏄剧ず鐢宠鍙戠エ鎸夐挳 -->
+          <button 
+            v-if="canApplyInvoice" 
+            class="apply-invoice-btn"
+            @click="handleApplyInvoice"
+          >
+            <text class="cuIcon-form"></text> 鐢宠鍙戠エ
+          </button>
+        </view>
         <view class="info-item" v-if="taskDetail.emergencyInfo.transferDistance">
           <view class="label">杞繍鍏噷鏁�</view>
           <view class="value">{{ taskDetail.emergencyInfo.transferDistance }}鍏噷</view>
@@ -566,6 +576,7 @@
   import { checkVehicleActiveTasks } from '@/api/task'
   import { getPaymentInfo } from '@/api/payment'
   import { getDicts } from '@/api/dict'
+  import { checkTaskInvoice } from '@/api/invoice'
   import { formatDateTime } from '@/utils/common'
   import { validateTaskForDepart, validateTaskForSettlement, getTaskVehicleId, checkTaskCanDepart } from '@/utils/taskValidator'
   import AttachmentUpload from './components/AttachmentUpload.vue'
@@ -587,7 +598,9 @@
         forceCompleteForm: {
           actualStartTime: '',
           actualEndTime: ''
-        }
+        },
+        hasInvoiceApplied: false, // 鏄惁宸茬敵璇峰彂绁�
+        invoiceStatus: null // 鍙戠エ鐘舵�侊細0-寰呭鏍�, 1-宸查�氳繃, 2-宸查┏鍥�
       }
     },
     computed: {
@@ -597,6 +610,16 @@
           return false
         }
         return ['COMPLETED', 'CANCELLED'].includes(this.taskDetail.taskStatus)
+      },
+      
+      // 鏄惁鍙互鐢宠鍙戠エ
+      canApplyInvoice() {
+        // 浠呮�ユ晳杞繍浠诲姟
+        if (this.taskDetail?.taskType !== 'EMERGENCY_TRANSFER') return false
+        // 浠诲姟蹇呴』宸插畬鎴�
+        if (this.taskDetail?.taskStatus !== 'COMPLETED') return false
+        // 鏈敵璇疯繃鍙戠エ锛屾垨鏇捐椹冲洖
+        return !this.hasInvoiceApplied || this.invoiceStatus === 2
       },
       
       // 鐢熸垚鎵ц浜哄憳瑙掕壊鏍囩鐨勭被鍚�
@@ -699,6 +722,8 @@
       this.taskId = options.id
       this.loadTaskDetail()
       this.loadCancelReasonDict() // 鍔犺浇鍙栨秷鍘熷洜瀛楀吀
+      // 妫�鏌ュ彂绁ㄧ敵璇风姸鎬�
+      this.checkInvoiceStatus()
     },
     onShow() {
       // 姣忔椤甸潰鏄剧ず鏃堕噸鏂板姞杞芥暟鎹紝纭繚浠庣紪杈戦〉闈㈣繑鍥炲悗鑳界湅鍒版渶鏂版暟鎹�
@@ -1058,6 +1083,45 @@
         }
         
         return null;
+      },
+      
+      // 妫�鏌ュ彂绁ㄧ敵璇风姸鎬�
+      checkInvoiceStatus() {
+        if (!this.taskId) return;
+        
+        // 璋冪敤鍚庣鎺ュ彛妫�鏌ヨ浠诲姟鏄惁宸茬敵璇峰彂绁�
+        checkTaskInvoice(this.taskId).then(response => {
+          if (response.code === 200 && response.data) {
+            this.hasInvoiceApplied = true;
+            this.invoiceStatus = response.data.status;
+          }
+        }).catch(error => {
+          console.error('妫�鏌ュ彂绁ㄧ敵璇风姸鎬佸け璐�:', error);
+          // 蹇界暐閿欒锛岄粯璁ゆ湭鐢宠
+        });
+      },
+      
+      // 鐢宠鍙戠エ
+      handleApplyInvoice() {
+        // 鍑嗗浠诲姟淇℃伅
+        const taskInfo = {
+          taskId: this.taskDetail.taskId,
+          taskCode: this.taskDetail.showTaskCode || this.taskDetail.taskCode,
+          legacyServiceOrderId: this.taskDetail.emergencyInfo?.legacyServiceOrdId,
+          serviceCode: this.taskDetail.emergencyInfo?.serviceCode,
+          departure: this.taskDetail.departureAddress,
+          destination: this.taskDetail.destinationAddress,
+          completionTime: this.formatTime(this.taskDetail.actualEndTime),
+          transferPrice: this.paymentInfo?.transferPrice || this.paymentInfo?.totalAmount
+        };
+        
+        // 灏嗕换鍔′俊鎭簭鍒楀寲涓� URL 鍙傛暟
+        const taskInfoParam = encodeURIComponent(JSON.stringify(taskInfo));
+        
+        // 璺宠浆鍒板彂绁ㄧ敵璇烽〉闈紝浼犻�掍换鍔′俊鎭�
+        uni.navigateTo({
+          url: `/pages/mine/invoice/apply?taskInfo=${taskInfoParam}`
+        });
       },
       
       // 鏇存柊浠诲姟鐘舵��
@@ -2310,6 +2374,20 @@
       margin-left: 10rpx;
       vertical-align: middle;
     }
+          
+    .apply-invoice-btn {
+      padding: 8rpx 16rpx;
+      font-size: 24rpx;
+      color: #fff;
+      background-color: #34C759;
+      border: none;
+      border-radius: 6rpx;
+      margin-left: 20rpx;
+    }
+          
+    .apply-invoice-btn::after {
+      border: none;
+    }
     
     // 鍙栨秷鍘熷洜瀵硅瘽妗嗘牱寮�
     .cancel-dialog {
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysInvoiceController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysInvoiceController.java
new file mode 100644
index 0000000..088027a
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysInvoiceController.java
@@ -0,0 +1,234 @@
+package com.ruoyi.web.controller.system;
+
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.apache.commons.lang3.StringUtils;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.entity.SysDept;
+import com.ruoyi.system.service.ISysDeptService;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.system.domain.SysInvoice;
+import com.ruoyi.system.service.ISysInvoiceService;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.utils.SecurityUtils;
+
+/**
+ * 鍙戠エ鐢宠Controller
+ * 
+ * @author ruoyi
+ * @date 2026-02-02
+ */
+@RestController
+@RequestMapping("/system/invoice")
+public class SysInvoiceController extends BaseController
+{
+    @Autowired
+    private ISysInvoiceService sysInvoiceService;
+    
+    @Autowired
+    private ISysDeptService sysDeptService;
+
+    /**
+     * 鏌ヨ鍙戠エ鐢宠鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('system:invoice:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(SysInvoice sysInvoice)
+    {
+        startPage();
+        List<SysInvoice> list = sysInvoiceService.selectSysInvoiceList(sysInvoice);
+        return getDataTable(list);
+    }
+
+    /**
+     * App绔煡璇㈡垜鐨勫彂绁ㄧ敵璇峰垪琛�
+     */
+    @GetMapping("/myList")
+    public TableDataInfo myList(SysInvoice sysInvoice)
+    {
+        sysInvoice.setApplyUserId(SecurityUtils.getUserId());
+        startPage();
+        List<Map<String, Object>> list = sysInvoiceService.selectMyInvoiceList(sysInvoice);
+        return getDataTable(list);
+    }
+
+    /**
+     * 瀵煎嚭鍙戠エ鐢宠鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('system:invoice:export')")
+    @Log(title = "鍙戠エ鐢宠", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, SysInvoice sysInvoice)
+    {
+        List<SysInvoice> list = sysInvoiceService.selectSysInvoiceList(sysInvoice);
+        ExcelUtil<SysInvoice> util = new ExcelUtil<SysInvoice>(SysInvoice.class);
+        util.exportExcel(response, list, "鍙戠エ鐢宠鏁版嵁");
+    }
+
+    /**
+     * 鑾峰彇鍙戠エ鐢宠璇︾粏淇℃伅
+     */
+    @PreAuthorize("@ss.hasAnyPermi('system:invoice:query, system:invoice:edit')")
+    @GetMapping(value = "/{invoiceId}")
+    public AjaxResult getInfo(@PathVariable("invoiceId") Long invoiceId)
+    {
+        return AjaxResult.success(sysInvoiceService.selectSysInvoiceByInvoiceId(invoiceId));
+    }
+
+    /**
+     * 鏂板鍙戠エ鐢宠
+     */
+    @Log(title = "鍙戠エ鐢宠", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody SysInvoice sysInvoice)
+    {
+        sysInvoice.setApplyUserId(SecurityUtils.getUserId());
+        return toAjax(sysInvoiceService.insertSysInvoice(sysInvoice));
+    }
+
+    /**
+     * 淇敼鍙戠エ鐢宠
+     */
+    @PreAuthorize("@ss.hasPermi('system:invoice:edit')")
+    @Log(title = "鍙戠エ鐢宠", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody SysInvoice sysInvoice)
+    {
+        return toAjax(sysInvoiceService.updateSysInvoice(sysInvoice));
+    }
+
+    /**
+     * 鍒犻櫎鍙戠エ鐢宠
+     */
+    @PreAuthorize("@ss.hasPermi('system:invoice:remove')")
+    @Log(title = "鍙戠エ鐢宠", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{invoiceIds}")
+    public AjaxResult remove(@PathVariable Long[] invoiceIds)
+    {
+        return toAjax(sysInvoiceService.deleteSysInvoiceByInvoiceIds(invoiceIds));
+    }
+
+    /**
+     * 鎵嬪姩瑙﹀彂鍚屾 (浠呴檺绠$悊鍛�)
+     */
+    @PreAuthorize("@ss.hasRole('admin')")
+    @GetMapping("/syncStatus")
+    public AjaxResult syncStatus() {
+        sysInvoiceService.syncStatusFromLegacySystem();
+        return AjaxResult.success("鍚屾浠诲姟宸茶Е鍙�");
+    }
+
+    /**
+     * 鍚屾鍗曚釜鍙戠エ鍒版棫绯荤粺
+     * @param invoiceId 鍙戠エID
+     */
+    @PreAuthorize("@ss.hasPermi('system:invoice:edit')")
+    @Log(title = "鍙戠エ鐢宠", businessType = BusinessType.UPDATE)
+    @PostMapping("/syncToLegacy/{invoiceId}")
+    public AjaxResult syncToLegacy(@PathVariable Long invoiceId) {
+        SysInvoice invoice = sysInvoiceService.selectSysInvoiceByInvoiceId(invoiceId);
+        if (invoice == null) {
+            return AjaxResult.error("鍙戠エ涓嶅瓨鍦�");
+        }
+        if (invoice.getStatus() != 1) {
+            return AjaxResult.error("鍙湁瀹℃牳閫氳繃鐨勫彂绁ㄦ墠鑳藉悓姝�");
+        }
+        
+        try {
+            sysInvoiceService.syncToLegacySystem(invoice.getInvoiceId());
+            return AjaxResult.success("鍚屾鎴愬姛");
+        } catch (Exception e) {
+            return AjaxResult.error("鍚屾澶辫触: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 妫�鏌ヤ换鍔℃槸鍚﹀凡鐢宠鍙戠エ
+     * @param taskId 浠诲姟ID
+     */
+    @GetMapping("/checkTaskInvoice/{taskId}")
+    public AjaxResult checkTaskInvoice(@PathVariable Long taskId) {
+        SysInvoice query = new SysInvoice();
+        query.setServiceOrderId(taskId);
+        List<SysInvoice> list = sysInvoiceService.selectSysInvoiceList(query);
+        
+        if (list != null && !list.isEmpty()) {
+            // 鍙繑鍥炴渶鏂扮殑涓�鏉¤褰�
+            SysInvoice invoice = list.get(0);
+            Map<String, Object> result = new HashMap<>();
+            result.put("hasApplied", true);
+            result.put("status", invoice.getStatus());
+            result.put("invoiceId", invoice.getInvoiceId());
+            return AjaxResult.success(result);
+        }
+        
+        return AjaxResult.success(null);
+    }
+
+    /**
+     * 鑾峰彇鍙敵璇峰彂绁ㄧ殑浠诲姟鍒楄〃
+     * @param searchKeyword 鎼滅储鍏抽敭璇嶏紙鏀寔taskCode銆乻erviceCode銆乴egacyServiceOrdNo锛�
+     * @param serviceOrdClass 鍒嗗叕鍙镐唬鐮侊紙鍙�夛紝榛樿浣跨敤鐢ㄦ埛鎵�灞炲垎鍏徃锛�
+     */
+    @GetMapping("/selectableTasks")
+    public AjaxResult getSelectableTasks(
+        @RequestParam(required = false) String searchKeyword,
+        @RequestParam(required = false) String serviceOrdClass
+    )
+    {
+        Long userId = SecurityUtils.getUserId();
+        // 鍘婚櫎鎼滅储鍏抽敭璇嶇殑绌烘牸
+        if (StringUtils.isNotBlank(searchKeyword)) {
+            searchKeyword = searchKeyword.trim();
+        }
+        
+        // 濡傛灉鏈寚瀹氬垎鍏徃锛岃嚜鍔ㄨ幏鍙栫敤鎴锋墍灞炲垎鍏徃
+        if (StringUtils.isBlank(serviceOrdClass)) {
+            try {
+                Long deptId = SecurityUtils.getLoginUser().getUser().getDeptId();
+                if (deptId != null) {
+                    SysDept dept = sysDeptService.selectDeptById(deptId);
+                    if (dept != null) {
+                        // 鍒ゆ柇鏄惁涓哄垎鍏徃锛坧arent_id = 100锛�
+                        if (dept.getParentId() != null && dept.getParentId() == 100) {
+                            serviceOrdClass = dept.getServiceOrderClass();
+                        } else if (dept.getAncestors() != null) {
+                            // 浠� ancestors 瑙f瀽鍒嗗叕鍙窱D
+                            String[] ancestorIds = dept.getAncestors().split(",");
+                            for (int i = 0; i < ancestorIds.length; i++) {
+                                if ("100".equals(ancestorIds[i]) && i + 1 < ancestorIds.length) {
+                                    Long branchId = Long.parseLong(ancestorIds[i + 1]);
+                                    SysDept branchDept = sysDeptService.selectDeptById(branchId);
+                                    if (branchDept != null) {
+                                        serviceOrdClass = branchDept.getServiceOrderClass();
+                                    }
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                // 鑾峰彇澶辫触涓嶅奖鍝嶆煡璇紝鍙槸涓嶈繃婊ゅ垎鍏徃
+            }
+        }
+        
+        return AjaxResult.success(sysInvoiceService.selectSelectableTasks(userId, searchKeyword, serviceOrdClass));
+    }
+}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskController.java
index 36a79da..bb566c9 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskController.java
@@ -78,6 +78,9 @@
     
     @Autowired
     private ITaskDispatchSyncService taskDispatchSyncService;
+    
+    @Autowired
+    private ITaskStatusPushService taskStatusPushService;
 
     /**
      * 鏌ヨ浠诲姟绠$悊鍒楄〃锛堝悗鍙扮鐞嗙锛�
@@ -704,4 +707,50 @@
             return error("鍚屾寮傚父: " + e.getMessage());
         }
     }
+    
+    /**
+     * 鎵嬪姩鍚屾浠诲姟鐘舵�佸埌鏃х郴缁�
+     * 褰撲换鍔$姸鎬佸彉鏇村悗鐢变簬缃戠粶绛夊師鍥犳湭鍚屾鍒版棫绯荤粺鏃讹紝鍙互閫氳繃姝ゆ帴鍙f墜鍔ㄨЕ鍙戝悓姝�
+     */
+//    @PreAuthorize("@ss.hasPermi('task:general:edit')")
+    @Log(title = "鎵嬪姩鍚屾浠诲姟鐘舵��", businessType = BusinessType.UPDATE)
+    @PostMapping("/syncTaskStatus/{taskId}")
+    public AjaxResult syncTaskStatus(@PathVariable Long taskId) {
+        try {
+            // 鏌ヨ浠诲姟淇℃伅
+            SysTask task = sysTaskService.selectSysTaskByTaskId(taskId);
+            if (task == null) {
+                return error("浠诲姟涓嶅瓨鍦�");
+            }
+            
+            // 鍙敮鎸佹�ユ晳杞繍浠诲姟
+            if (!"EMERGENCY_TRANSFER".equals(task.getTaskType())) {
+                return error("鍙湁鎬ユ晳杞繍浠诲姟鎵嶈兘鍚屾鍒版棫绯荤粺");
+            }
+            
+            // 鏌ヨ鎬ユ晳杞繍鎵╁睍淇℃伅
+            SysTaskEmergency emergency = sysTaskEmergencyService.selectSysTaskEmergencyByTaskId(taskId);
+            if (emergency == null) {
+                return error("鎬ユ晳杞繍鎵╁睍淇℃伅涓嶅瓨鍦�");
+            }
+            
+            // 蹇呴』鍏堟湁璋冨害鍗�
+            if (emergency.getLegacyDispatchOrdId() == null || emergency.getLegacyDispatchOrdId() <= 0) {
+                return error("璇峰厛鍚屾璋冨害鍗曪紝浠诲姟鐘舵�佷俊鎭悓姝ュ埌鏃х郴缁熺殑璋冨害鍗曚腑");
+            }
+            
+            // 璋冪敤鐘舵�佸悓姝ユ湇鍔�
+            boolean success = taskStatusPushService.pushTaskStatusToLegacy(taskId);
+            
+            if (success) {
+                return success("浠诲姟鐘舵�佸悓姝ユ垚鍔�");
+            } else {
+                return error("浠诲姟鐘舵�佸悓姝ュけ璐ワ紝璇锋煡鐪嬫棩蹇楄幏鍙栬缁嗕俊鎭�");
+            }
+            
+        } catch (Exception e) {
+            logger.error("鎵嬪姩鍚屾浠诲姟鐘舵�佸紓甯革紝taskId: {}", taskId, e);
+            return error("鍚屾寮傚父: " + e.getMessage());
+        }
+    }
 }
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index fed985b..ffcd5be 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: dev
+    active: prod
   # 鏂囦欢涓婁紶
   servlet:
     multipart:
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/SysInvoiceSyncTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/SysInvoiceSyncTask.java
new file mode 100644
index 0000000..0f03d16
--- /dev/null
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/SysInvoiceSyncTask.java
@@ -0,0 +1,26 @@
+package com.ruoyi.quartz.task;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import com.ruoyi.system.service.ISysInvoiceService;
+
+/**
+ * 鍙戠エ鍚屾瀹氭椂浠诲姟 - 鍚屾鏃х郴缁熷彂绁ㄤ俊鎭埌鏂扮郴缁�
+ * 
+ * @author ruoyi
+ */
+@Component("sysInvoiceSyncTask")
+public class SysInvoiceSyncTask
+{
+    @Autowired
+    private ISysInvoiceService sysInvoiceService;
+
+
+    /**
+     * 鍚屾鏃х郴缁熷彂绁ㄧ姸鎬�
+     */
+    public void syncStatus()
+    {
+        sysInvoiceService.syncStatusFromLegacySystem();
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/SysInvoiceTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/SysInvoiceTask.java
new file mode 100644
index 0000000..1e75c5d
--- /dev/null
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/SysInvoiceTask.java
@@ -0,0 +1,33 @@
+package com.ruoyi.quartz.task;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import com.ruoyi.system.service.ISysInvoiceService;
+
+/**
+ * 鍙戠エ鍚屾瀹氭椂浠诲姟
+ * 
+ * @author ruoyi
+ */
+@Component("sysInvoiceTask")
+public class SysInvoiceTask
+{
+    @Autowired
+    private ISysInvoiceService sysInvoiceService;
+
+    /**
+     * 鍚屾鏃х郴缁熷彂绁ㄧ姸鎬�
+     */
+    public void syncStatus()
+    {
+        sysInvoiceService.syncStatusFromLegacySystem();
+    }
+    
+    /**
+     * 浠庢棫绯荤粺鍚屾鍙戠エ淇℃伅鍒版柊绯荤粺
+     */
+    public void syncInvoiceFromLegacySystem()
+    {
+//        sysInvoiceService.syncInvoiceFromLegacySystem();
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysInvoice.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysInvoice.java
new file mode 100644
index 0000000..30e6fa9
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysInvoice.java
@@ -0,0 +1,380 @@
+package com.ruoyi.system.domain;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 鍙戠エ鐢宠瀵硅薄 sys_invoice
+ * 
+ * @author ruoyi
+ * @date 2026-02-02
+ */
+public class SysInvoice extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 鍙戠エID */
+    private Long invoiceId;
+
+    /** 鏈嶅姟鍗曞彿(鏂扮郴缁烮D) */
+    @Excel(name = "鏈嶅姟鍗曞彿(鏂�)")
+    private Long serviceOrderId;
+
+    /** 鏃х郴缁熸湇鍔″崟鍙� */
+    @Excel(name = "鏈嶅姟鍗曞彿(鏃�)")
+    private Long legacyServiceOrderId;
+
+    /** 寮�绁ㄧ被鍨�(1-涓汉, 2-浼佷笟) */
+    @Excel(name = "寮�绁ㄧ被鍨�", readConverterExp = "1=涓汉,2=浼佷笟")
+    private Integer invoiceType;
+
+    /** 鍙戠エ鎶ご */
+    @Excel(name = "鍙戠エ鎶ご")
+    private String invoiceName;
+
+    /** 鍙戠エ閲戦 */
+    @Excel(name = "鍙戠エ閲戦")
+    private BigDecimal invoiceMoney;
+
+    /** 鍙戠エ澶囨敞 */
+    @Excel(name = "鍙戠エ澶囨敞")
+    private String invoiceRemarks;
+
+    /** 浼佷笟娉ㄥ唽鍦板潃 */
+    @Excel(name = "浼佷笟娉ㄥ唽鍦板潃")
+    private String companyAddress;
+
+    /** 浼佷笟寮�鎴烽摱琛� */
+    @Excel(name = "浼佷笟寮�鎴烽摱琛�")
+    private String companyBank;
+
+    /** 浼佷笟閾惰甯愬彿 */
+    @Excel(name = "浼佷笟閾惰甯愬彿")
+    private String companyBankNo;
+
+    /** 閭紪 */
+    @Excel(name = "閭紪")
+    private String zipCode;
+
+    /** 閭瘎鍦板潃 */
+    @Excel(name = "閭瘎鍦板潃")
+    private String mailAddress;
+
+    /** 鑱旂郴浜� */
+    @Excel(name = "鑱旂郴浜�")
+    private String contactName;
+
+    /** 鑱旂郴鐢佃瘽 */
+    @Excel(name = "鑱旂郴鐢佃瘽")
+    private String contactPhone;
+
+    /** 鑱旂郴閭 */
+    @Excel(name = "鑱旂郴閭")
+    private String contactEmail;
+
+    /** 鐢宠鐘舵��(0-寰呭鏍�, 1-宸查�氳繃, 2-宸查┏鍥�) */
+    @Excel(name = "鐢宠鐘舵��", readConverterExp = "0=寰呭鏍�,1=宸查�氳繃,2=宸查┏鍥�")
+    private Integer status;
+
+    /** 鍙戠エ缂栧彿 */
+    @Excel(name = "鍙戠エ缂栧彿")
+    private String invoiceNo;
+
+    /** 鍙戠エ閾炬帴 */
+    @Excel(name = "鍙戠エ閾炬帴")
+    private String invoiceUrl;
+
+    /** 鐢宠浜篒D */
+    private Long applyUserId;
+
+    /** 鐢宠鏃堕棿 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "鐢宠鏃堕棿", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date applyTime;
+
+    /** 瀹℃牳浜篒D */
+    private Long auditUserId;
+
+    /** 瀹℃牳鏃堕棿 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "瀹℃牳鏃堕棿", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date auditTime;
+
+    /** 瀹℃牳澶囨敞 */
+    private String auditRemarks;
+
+    /** 鍚屾鐘舵��(0-鏈悓姝�, 1-宸插悓姝�, 2-澶辫触) */
+    private Integer syncStatus;
+
+    /** 鏃х郴缁熷彂绁↖D */
+    private Integer legacyInvoiceId;
+    
+    /** 鏈嶅姟鍗曞彿锛堟牸寮忓寲锛屽GZ20260202-001锛�- 浠呯敤浜庢煡璇㈣繑鍥� */
+    private String serviceCode;
+
+    public void setInvoiceId(Long invoiceId) 
+    {
+        this.invoiceId = invoiceId;
+    }
+
+    public Long getInvoiceId() 
+    {
+        return invoiceId;
+    }
+    public void setServiceOrderId(Long serviceOrderId) 
+    {
+        this.serviceOrderId = serviceOrderId;
+    }
+
+    public Long getServiceOrderId() 
+    {
+        return serviceOrderId;
+    }
+    public void setLegacyServiceOrderId(Long legacyServiceOrderId) 
+    {
+        this.legacyServiceOrderId = legacyServiceOrderId;
+    }
+
+    public Long getLegacyServiceOrderId() 
+    {
+        return legacyServiceOrderId;
+    }
+    public void setInvoiceType(Integer invoiceType) 
+    {
+        this.invoiceType = invoiceType;
+    }
+
+    public Integer getInvoiceType() 
+    {
+        return invoiceType;
+    }
+    public void setInvoiceName(String invoiceName) 
+    {
+        this.invoiceName = invoiceName;
+    }
+
+    public String getInvoiceName() 
+    {
+        return invoiceName;
+    }
+    public void setInvoiceMoney(BigDecimal invoiceMoney) 
+    {
+        this.invoiceMoney = invoiceMoney;
+    }
+
+    public BigDecimal getInvoiceMoney() 
+    {
+        return invoiceMoney;
+    }
+    public void setInvoiceRemarks(String invoiceRemarks) 
+    {
+        this.invoiceRemarks = invoiceRemarks;
+    }
+
+    public String getInvoiceRemarks() 
+    {
+        return invoiceRemarks;
+    }
+    public void setCompanyAddress(String companyAddress) 
+    {
+        this.companyAddress = companyAddress;
+    }
+
+    public String getCompanyAddress() 
+    {
+        return companyAddress;
+    }
+    public void setCompanyBank(String companyBank) 
+    {
+        this.companyBank = companyBank;
+    }
+
+    public String getCompanyBank() 
+    {
+        return companyBank;
+    }
+    public void setCompanyBankNo(String companyBankNo) 
+    {
+        this.companyBankNo = companyBankNo;
+    }
+
+    public String getCompanyBankNo() 
+    {
+        return companyBankNo;
+    }
+    public void setZipCode(String zipCode) 
+    {
+        this.zipCode = zipCode;
+    }
+
+    public String getZipCode() 
+    {
+        return zipCode;
+    }
+    public void setMailAddress(String mailAddress) 
+    {
+        this.mailAddress = mailAddress;
+    }
+
+    public String getMailAddress() 
+    {
+        return mailAddress;
+    }
+    public void setContactName(String contactName) 
+    {
+        this.contactName = contactName;
+    }
+
+    public String getContactName() 
+    {
+        return contactName;
+    }
+    public void setContactPhone(String contactPhone) 
+    {
+        this.contactPhone = contactPhone;
+    }
+
+    public String getContactPhone() 
+    {
+        return contactPhone;
+    }
+    public void setContactEmail(String contactEmail) 
+    {
+        this.contactEmail = contactEmail;
+    }
+
+    public String getContactEmail() 
+    {
+        return contactEmail;
+    }
+    public void setStatus(Integer status) 
+    {
+        this.status = status;
+    }
+
+    public Integer getStatus() 
+    {
+        return status;
+    }
+    public void setInvoiceNo(String invoiceNo) 
+    {
+        this.invoiceNo = invoiceNo;
+    }
+
+    public String getInvoiceNo() 
+    {
+        return invoiceNo;
+    }
+    public void setInvoiceUrl(String invoiceUrl) 
+    {
+        this.invoiceUrl = invoiceUrl;
+    }
+
+    public String getInvoiceUrl() 
+    {
+        return invoiceUrl;
+    }
+    public void setApplyUserId(Long applyUserId) 
+    {
+        this.applyUserId = applyUserId;
+    }
+
+    public Long getApplyUserId() 
+    {
+        return applyUserId;
+    }
+    public void setApplyTime(Date applyTime) 
+    {
+        this.applyTime = applyTime;
+    }
+
+    public Date getApplyTime() 
+    {
+        return applyTime;
+    }
+    public void setAuditUserId(Long auditUserId) 
+    {
+        this.auditUserId = auditUserId;
+    }
+
+    public Long getAuditUserId() 
+    {
+        return auditUserId;
+    }
+    public void setAuditTime(Date auditTime) 
+    {
+        this.auditTime = auditTime;
+    }
+
+    public Date getAuditTime() 
+    {
+        return auditTime;
+    }
+    public void setAuditRemarks(String auditRemarks) 
+    {
+        this.auditRemarks = auditRemarks;
+    }
+
+    public String getAuditRemarks() 
+    {
+        return auditRemarks;
+    }
+
+    public Integer getSyncStatus() {
+        return syncStatus;
+    }
+
+    public void setSyncStatus(Integer syncStatus) {
+        this.syncStatus = syncStatus;
+    }
+
+    public Integer getLegacyInvoiceId() {
+        return legacyInvoiceId;
+    }
+
+    public void setLegacyInvoiceId(Integer legacyInvoiceId) {
+        this.legacyInvoiceId = legacyInvoiceId;
+    }
+    
+    public String getServiceCode() {
+        return serviceCode;
+    }
+
+    public void setServiceCode(String serviceCode) {
+        this.serviceCode = serviceCode;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("invoiceId", getInvoiceId())
+            .append("serviceOrderId", getServiceOrderId())
+            .append("legacyServiceOrderId", getLegacyServiceOrderId())
+            .append("invoiceType", getInvoiceType())
+            .append("invoiceName", getInvoiceName())
+            .append("invoiceMoney", getInvoiceMoney())
+            .append("invoiceRemarks", getInvoiceRemarks())
+            .append("companyAddress", getCompanyAddress())
+            .append("companyBank", getCompanyBank())
+            .append("companyBankNo", getCompanyBankNo())
+            .append("zipCode", getZipCode())
+            .append("mailAddress", getMailAddress())
+            .append("contactName", getContactName())
+            .append("contactPhone", getContactPhone())
+            .append("contactEmail", getContactEmail())
+            .append("status", getStatus())
+            .append("invoiceNo", getInvoiceNo())
+            .append("invoiceUrl", getInvoiceUrl())
+            .append("applyUserId", getApplyUserId())
+            .append("applyTime", getApplyTime())
+            .append("auditUserId", getAuditUserId())
+            .append("auditTime", getAuditTime())
+            .append("auditRemarks", getAuditRemarks())
+            .toString();
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysTaskEmergency.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysTaskEmergency.java
index 80e7a9f..6b4ecd7 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysTaskEmergency.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysTaskEmergency.java
@@ -166,6 +166,10 @@
     /** 鍙栨秷鏃堕棿 */
     private java.util.Date cancelTime;
 
+    /**
+     * 鏈嶅姟鍗曠紪鍙�
+     * @return
+     */
     public String getServiceCode(){
         if(this.legacyServiceOrdClass!=null && this.legacyServiceNsTime!=null && this.legacyServiceOrdNo!=null) {
             String nstime = DateUtils.parseDateToStr(DateUtils.YYYYMMDD, this.legacyServiceNsTime);
@@ -174,6 +178,10 @@
         return null;
     }
 
+    /**
+     * 璋冨害鍗曠紪鍙�
+     * @return
+     */
     public String getDispatchCode(){
         if(this.legacyDispatchOrdClass!=null && this.legacyDispatchNsTime!=null && this.legacyDispatchOrdNo!=null) {
             String nstime = DateUtils.parseDateToStr(DateUtils.YYYYMMDD, this.legacyDispatchNsTime);
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/LegacyInvoiceMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/LegacyInvoiceMapper.java
new file mode 100644
index 0000000..de782d9
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/LegacyInvoiceMapper.java
@@ -0,0 +1,37 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+import java.util.Map;
+import com.ruoyi.common.annotation.DataSource;
+import com.ruoyi.common.enums.DataSourceType;
+
+/**
+ * 鏃х郴缁熷彂绁ㄦ暟鎹甅apper鎺ュ彛 (SQL Server)
+ * 
+ * @author ruoyi
+ * @date 2026-02-02
+ */
+@DataSource(DataSourceType.SQLSERVER)
+public interface LegacyInvoiceMapper 
+{
+    /**
+     * 鎻掑叆鍙戠エ鐢宠鍒版棫绯荤粺
+     * @param params
+     * @return
+     */
+    public int insertLegacyInvoice(Map<String, Object> params);
+
+    /**
+     * 鏌ヨ鏃х郴缁熷彂绁ㄧ姸鎬佸彉鍖栫殑鏁版嵁
+     * @param lastSyncTime
+     * @return
+     */
+    public List<Map<String, Object>> selectUpdatedInvoices(String lastSyncTime);
+
+    /**
+     * 鏍规嵁鏈嶅姟鍗旾D鏌ヨ鏃х郴缁熷彂绁ㄤ俊鎭�
+     * @param serviceOrderId
+     * @return
+     */
+    public List<Map<String, Object>> selectLegacyInvoiceByServiceOrderId(Long serviceOrderId);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysInvoiceMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysInvoiceMapper.java
new file mode 100644
index 0000000..c416ffa
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysInvoiceMapper.java
@@ -0,0 +1,91 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.ibatis.annotations.Param;
+import com.ruoyi.system.domain.SysInvoice;
+
+/**
+ * 鍙戠エ鐢宠Mapper鎺ュ彛
+ * 
+ * @author ruoyi
+ * @date 2026-02-02
+ */
+public interface SysInvoiceMapper 
+{
+    /**
+     * 鏌ヨ鍙戠エ鐢宠
+     * 
+     * @param invoiceId 鍙戠エ鐢宠涓婚敭
+     * @return 鍙戠エ鐢宠
+     */
+    public SysInvoice selectSysInvoiceByInvoiceId(Long invoiceId);
+
+    /**
+     * 鏌ヨ鍙戠エ鐢宠鍒楄〃
+     * 
+     * @param sysInvoice 鍙戠エ鐢宠
+     * @return 鍙戠エ鐢宠闆嗗悎
+     */
+    public List<SysInvoice> selectSysInvoiceList(SysInvoice sysInvoice);
+    
+    /**
+     * 鏌ヨ鎴戠殑鍙戠エ鐢宠鍒楄〃锛圓pp绔紝杩斿洖Map鍖呭惈serviceCode锛�
+     * 
+     * @param sysInvoice 鍙戠エ鐢宠
+     * @return 鍙戠エ鐢宠闆嗗悎
+     */
+    public List<Map<String, Object>> selectMyInvoiceList(SysInvoice sysInvoice);
+
+    /**
+     * 鏂板鍙戠エ鐢宠
+     * 
+     * @param sysInvoice 鍙戠エ鐢宠
+     * @return 缁撴灉
+     */
+    public int insertSysInvoice(SysInvoice sysInvoice);
+
+    /**
+     * 淇敼鍙戠エ鐢宠
+     * 
+     * @param sysInvoice 鍙戠エ鐢宠
+     * @return 缁撴灉
+     */
+    public int updateSysInvoice(SysInvoice sysInvoice);
+
+    /**
+     * 鍒犻櫎鍙戠エ鐢宠
+     * 
+     * @param invoiceId 鍙戠エ鐢宠涓婚敭
+     * @return 缁撴灉
+     */
+    public int deleteSysInvoiceByInvoiceId(Long invoiceId);
+
+    /**
+     * 鎵归噺鍒犻櫎鍙戠エ鐢宠
+     * 
+     * @param invoiceIds 闇�瑕佸垹闄ょ殑鏁版嵁涓婚敭闆嗗悎
+     * @return 缁撴灉
+     */
+    public int deleteSysInvoiceByInvoiceIds(Long[] invoiceIds);
+
+    /**
+     * 鏍规嵁鏃х郴缁熷彂绁↖D鏌ヨ
+     * @param legacyInvoiceId
+     * @return
+     */
+    public SysInvoice selectSysInvoiceByLegacyId(Integer legacyInvoiceId);
+
+    /**
+     * 鏌ヨ鍙敵璇峰彂绁ㄧ殑浠诲姟鍒楄〃
+     * @param userId 鐢ㄦ埛ID
+     * @param searchKeyword 鎼滅储鍏抽敭璇�
+     * @param serviceOrdClass 鍒嗗叕鍙镐唬鐮�
+     * @return
+     */
+    public List<Map<String, Object>> selectSelectableTasks(
+        @Param("userId") Long userId, 
+        @Param("searchKeyword") String searchKeyword,
+        @Param("serviceOrdClass") String serviceOrdClass
+    );
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysInvoiceService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysInvoiceService.java
new file mode 100644
index 0000000..5e0b1da
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysInvoiceService.java
@@ -0,0 +1,91 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import java.util.Map;
+import com.ruoyi.system.domain.SysInvoice;
+
+/**
+ * 鍙戠エ鐢宠Service鎺ュ彛
+ * 
+ * @author ruoyi
+ * @date 2026-02-02
+ */
+public interface ISysInvoiceService 
+{
+    /**
+     * 鏌ヨ鍙戠エ鐢宠
+     * 
+     * @param invoiceId 鍙戠エ鐢宠涓婚敭
+     * @return 鍙戠エ鐢宠
+     */
+    public SysInvoice selectSysInvoiceByInvoiceId(Long invoiceId);
+
+    /**
+     * 鏌ヨ鍙戠エ鐢宠鍒楄〃
+     * 
+     * @param sysInvoice 鍙戠エ鐢宠
+     * @return 鍙戠エ鐢宠闆嗗悎
+     */
+    public List<SysInvoice> selectSysInvoiceList(SysInvoice sysInvoice);
+    
+    /**
+     * 鏌ヨ鎴戠殑鍙戠エ鐢宠鍒楄〃锛圓pp绔紝杩斿洖Map鍖呭惈serviceCode锛�
+     * 
+     * @param sysInvoice 鍙戠エ鐢宠
+     * @return 鍙戠エ鐢宠闆嗗悎
+     */
+    public List<Map<String, Object>> selectMyInvoiceList(SysInvoice sysInvoice);
+
+    /**
+     * 鏂板鍙戠エ鐢宠
+     * 
+     * @param sysInvoice 鍙戠エ鐢宠
+     * @return 缁撴灉
+     */
+    public int insertSysInvoice(SysInvoice sysInvoice);
+
+    /**
+     * 淇敼鍙戠エ鐢宠
+     * 
+     * @param sysInvoice 鍙戠エ鐢宠
+     * @return 缁撴灉
+     */
+    public int updateSysInvoice(SysInvoice sysInvoice);
+
+    /**
+     * 鎵归噺鍒犻櫎鍙戠エ鐢宠
+     * 
+     * @param invoiceIds 闇�瑕佸垹闄ょ殑鍙戠エ鐢宠涓婚敭闆嗗悎
+     * @return 缁撴灉
+     */
+    public int deleteSysInvoiceByInvoiceIds(Long[] invoiceIds);
+
+    /**
+     * 鍒犻櫎鍙戠エ鐢宠淇℃伅
+     * 
+     * @param invoiceId 鍙戠エ鐢宠涓婚敭
+     * @return 缁撴灉
+     */
+    public int deleteSysInvoiceByInvoiceId(Long invoiceId);
+
+    /**
+     * 鍚屾鍒版棫绯荤粺
+     * @param invoiceId
+     * @return
+     */
+    public int syncToLegacySystem(Long invoiceId);
+
+    /**
+     * 浠庢棫绯荤粺鍚屾鐘舵��
+     */
+    public void syncStatusFromLegacySystem();
+
+    /**
+     * 鏌ヨ鍙敵璇峰彂绁ㄧ殑浠诲姟鍒楄〃
+     * @param userId 鐢ㄦ埛ID
+     * @param searchKeyword 鎼滅储鍏抽敭璇�
+     * @param serviceOrdClass 鍒嗗叕鍙镐唬鐮�
+     * @return
+     */
+    public List<Map<String, Object>> selectSelectableTasks(Long userId, String searchKeyword, String serviceOrdClass);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentSyncServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentSyncServiceImpl.java
index 499a94b..f927f3c 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentSyncServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentSyncServiceImpl.java
@@ -120,7 +120,11 @@
             }
             paidMoney.setPaidMoney(payment.getSettlementAmount());
             paidMoney.setPaidMoneyType(convertPaymentMethodToLegacy(payment.getPaymentMethod()));
-            paidMoney.setPaidMoneyMono(payment.getTradeNo() != null ? payment.getTradeNo() : payment.getOutTradeNo());
+            String outTradeNo = payment.getTradeNo() != null ? payment.getTradeNo() : payment.getOutTradeNo();
+            if(!outTradeNo.contains("[鏀粯涓撶敤]")){
+                outTradeNo=outTradeNo+"[鏀粯涓撶敤]";
+            }
+            paidMoney.setPaidMoneyMono(outTradeNo);
             paidMoney.setPaidMoneyTime(payment.getPayTime() != null ? payment.getPayTime() : new Date());
             paidMoney.setPaidMoneyOaID(oaUserId);
             paidMoney.setPaidMoneyUnitID(0); // 榛樿涓�0
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysInvoiceServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysInvoiceServiceImpl.java
new file mode 100644
index 0000000..ee2c483
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysInvoiceServiceImpl.java
@@ -0,0 +1,243 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.system.domain.SysInvoice;
+import com.ruoyi.system.mapper.SysInvoiceMapper;
+import com.ruoyi.system.mapper.LegacyInvoiceMapper;
+import com.ruoyi.system.mapper.SysUserMapper;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.system.service.ISysInvoiceService;
+
+/**
+ * 鍙戠エ鐢宠Service涓氬姟灞傚鐞�
+ * 
+ * @author ruoyi
+ * @date 2026-02-02
+ */
+@Service
+public class SysInvoiceServiceImpl implements ISysInvoiceService 
+{
+    private static final Logger log = LoggerFactory.getLogger(SysInvoiceServiceImpl.class);
+
+    @Autowired
+    private SysInvoiceMapper sysInvoiceMapper;
+
+    @Autowired
+    private LegacyInvoiceMapper legacyInvoiceMapper;
+    
+    @Autowired
+    private SysUserMapper sysUserMapper;
+
+    /**
+     * 鏌ヨ鍙戠エ鐢宠
+     * 
+     * @param invoiceId 鍙戠エ鐢宠涓婚敭
+     * @return 鍙戠エ鐢宠
+     */
+    @Override
+    public SysInvoice selectSysInvoiceByInvoiceId(Long invoiceId)
+    {
+        return sysInvoiceMapper.selectSysInvoiceByInvoiceId(invoiceId);
+    }
+
+    /**
+     * 鏌ヨ鍙戠エ鐢宠鍒楄〃
+     * 
+     * @param sysInvoice 鍙戠エ鐢宠
+     * @return 鍙戠エ鐢宠
+     */
+    @Override
+    public List<SysInvoice> selectSysInvoiceList(SysInvoice sysInvoice)
+    {
+        return sysInvoiceMapper.selectSysInvoiceList(sysInvoice);
+    }
+    
+    /**
+     * 鏌ヨ鎴戠殑鍙戠エ鐢宠鍒楄〃锛圓pp绔紝杩斿洖Map鍖呭惈serviceCode锛�
+     * 
+     * @param sysInvoice 鍙戠エ鐢宠
+     * @return 鍙戠エ鐢宠
+     */
+    @Override
+    public List<Map<String, Object>> selectMyInvoiceList(SysInvoice sysInvoice)
+    {
+        return sysInvoiceMapper.selectMyInvoiceList(sysInvoice);
+    }
+
+    /**
+     * 鏂板鍙戠エ鐢宠
+     * 
+     * @param sysInvoice 鍙戠エ鐢宠
+     * @return 缁撴灉
+     */
+    @Override
+    public int insertSysInvoice(SysInvoice sysInvoice)
+    {
+        sysInvoice.setApplyTime(DateUtils.getNowDate());
+        sysInvoice.setStatus(0); // 寰呭鏍�
+        sysInvoice.setSyncStatus(0); // 鏈悓姝�
+        int rows = sysInvoiceMapper.insertSysInvoice(sysInvoice);
+        
+        // 鑷姩灏濊瘯鍚屾鍒版棫绯荤粺
+        if (rows > 0) {
+            try {
+                syncToLegacySystem(sysInvoice.getInvoiceId());
+            } catch (Exception e) {
+                log.error("鍚屾鍙戠エ鐢宠鍒版棫绯荤粺澶辫触", e);
+            }
+        }
+        return rows;
+    }
+
+    /**
+     * 淇敼鍙戠エ鐢宠
+     * 
+     * @param sysInvoice 鍙戠エ鐢宠
+     * @return 缁撴灉
+     */
+    @Override
+    public int updateSysInvoice(SysInvoice sysInvoice)
+    {
+        return sysInvoiceMapper.updateSysInvoice(sysInvoice);
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎鍙戠エ鐢宠
+     * 
+     * @param invoiceIds 闇�瑕佸垹闄ょ殑鍙戠エ鐢宠涓婚敭
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteSysInvoiceByInvoiceIds(Long[] invoiceIds)
+    {
+        return sysInvoiceMapper.deleteSysInvoiceByInvoiceIds(invoiceIds);
+    }
+
+    /**
+     * 鍒犻櫎鍙戠エ鐢宠淇℃伅
+     * 
+     * @param invoiceId 鍙戠エ鐢宠涓婚敭
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteSysInvoiceByInvoiceId(Long invoiceId)
+    {
+        return sysInvoiceMapper.deleteSysInvoiceByInvoiceId(invoiceId);
+    }
+
+    /**
+     * 鍚屾鍙戠エ鐢宠鍒版棫绯荤粺 (SQL Server)
+     */
+    @Override
+    public int syncToLegacySystem(Long invoiceId) {
+        SysInvoice invoice = sysInvoiceMapper.selectSysInvoiceByInvoiceId(invoiceId);
+        if (invoice == null) return 0;
+
+        Map<String, Object> params = new HashMap<>();
+        params.put("ServiceOrderIDPK", invoice.getLegacyServiceOrderId());
+        params.put("InvoiceType", invoice.getInvoiceType());
+        params.put("InvoiceName", invoice.getInvoiceName());
+        params.put("InvoiceMakeout", invoice.getInvoiceRemarks());
+        params.put("InvoiceCompanyPhone", invoice.getContactPhone());
+        params.put("InvoiceCompanyID", ""); // 绋庡彿瀛楁鑻ユ湁鍙ˉ
+        params.put("InvoiceCompanyAdd", invoice.getCompanyAddress());
+        params.put("InvoiceCompanyBank", invoice.getCompanyBank());
+        params.put("InvoiceCompanyBankNo", invoice.getCompanyBankNo());
+        params.put("InvoiceZipCode", invoice.getZipCode());
+        params.put("Invoice_strAdd", invoice.getMailAddress());
+        params.put("Invoice_strName", invoice.getContactName());
+        params.put("Invoice_strPhone", invoice.getContactPhone());
+        params.put("Invoice_strEmail", invoice.getContactEmail());
+        params.put("InvoiceMoney", invoice.getInvoiceMoney());
+        
+        // 閫氳繃鍒涘缓浜築ID鏌ヨOA鐢ㄦ埛ID
+        Integer oaUserId = 0;
+        if (invoice.getApplyUserId() != null) {
+            SysUser user = sysUserMapper.selectUserById(invoice.getApplyUserId());
+            if (user != null && user.getOaUserId() != null) {
+                oaUserId = user.getOaUserId();
+            }
+        }
+        params.put("ApplyOAID", oaUserId);
+
+        try {
+            int rows = legacyInvoiceMapper.insertLegacyInvoice(params);
+            if (rows > 0) {
+                // SQL Server insert 浼氶�氳繃 useGeneratedKeys 杩斿洖鑷 ID 鍒� Map 鐨� keyProperty
+                Object legacyId = params.get("InvoiceID");
+                if (legacyId != null) {
+                    invoice.setLegacyInvoiceId(Integer.valueOf(legacyId.toString()));
+                    invoice.setSyncStatus(1); // 宸插悓姝�
+                    sysInvoiceMapper.updateSysInvoice(invoice);
+                }
+            }
+            return rows;
+        } catch (Exception e) {
+            log.error("鍚屾鍙戠エ鍒版棫绯荤粺寮傚父: {}", e.getMessage());
+            invoice.setSyncStatus(2); // 鍚屾澶辫触
+            sysInvoiceMapper.updateSysInvoice(invoice);
+            throw e;
+        }
+    }
+
+    /**
+     * 浠庢棫绯荤粺鍚屾鍙戠エ鐘舵�佸彉鍖�
+     */
+    @Override
+    public void syncStatusFromLegacySystem() {
+        // 鏌ヨ鏈�杩�3澶╂湁鍙樺寲鐨勬暟鎹�
+        String lastSyncTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.addDays(new Date(), -3));
+        List<Map<String, Object>> updatedList = legacyInvoiceMapper.selectUpdatedInvoices(lastSyncTime);
+        
+        for (Map<String, Object> legacyData : updatedList) {
+            Integer legacyId = (Integer) legacyData.get("InvoiceID");
+            SysInvoice invoice = sysInvoiceMapper.selectSysInvoiceByLegacyId(legacyId);
+            
+            if (invoice != null) {
+                // 鐘舵�佹槧灏勶細鏃х郴缁� AuditStatus (鍋囪 3=閫氳繃, 4=鎷掔粷, 鍏朵粬=鐢宠涓�)
+                Integer auditStatus = (Integer) legacyData.get("AuditStatus");
+                if (auditStatus != null) {
+                    if (auditStatus == 3) invoice.setStatus(1); // 宸查�氳繃
+                    else if (auditStatus == 4) invoice.setStatus(2); // 宸查┏鍥�
+                }
+                
+                // 鏇存柊鍏朵粬淇℃伅
+                if (legacyData.get("InvoiceNo") != null) {
+                    invoice.setInvoiceNo(legacyData.get("InvoiceNo").toString());
+                }
+                
+                // 鍙戠エ鏂囦欢閾炬帴 (浼樺厛鍙� EleCloud_PDF)
+                String pdf = legacyData.get("EleCloud_PDF") != null ? legacyData.get("EleCloud_PDF").toString() : null;
+                String url = legacyData.get("InvoiceURL") != null ? legacyData.get("InvoiceURL").toString() : null;
+                invoice.setInvoiceUrl(pdf != null && !pdf.isEmpty() ? pdf : url);
+                
+                if (legacyData.get("AuditTime") != null) {
+                    invoice.setAuditTime((Date) legacyData.get("AuditTime"));
+                }
+                if (legacyData.get("AuditMakeout") != null) {
+                    invoice.setAuditRemarks(legacyData.get("AuditMakeout").toString());
+                }
+                
+                sysInvoiceMapper.updateSysInvoice(invoice);
+            }
+        }
+    }
+
+    /**
+     * 鏌ヨ鍙�夋嫨鐨勪换鍔″垪琛�
+     */
+    @Override
+    public List<Map<String, Object>> selectSelectableTasks(Long userId, String searchKeyword, String serviceOrdClass) {
+        return sysInvoiceMapper.selectSelectableTasks(userId, searchKeyword, serviceOrdClass);
+    }
+}
diff --git a/ruoyi-system/src/main/resources/mapper/system/LegacyInvoiceMapper.xml b/ruoyi-system/src/main/resources/mapper/system/LegacyInvoiceMapper.xml
new file mode 100644
index 0000000..b36a98b
--- /dev/null
+++ b/ruoyi-system/src/main/resources/mapper/system/LegacyInvoiceMapper.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.LegacyInvoiceMapper">
+    
+    <insert id="insertLegacyInvoice" parameterType="Map" useGeneratedKeys="true" keyProperty="InvoiceID">
+        INSERT INTO InvoiceData (
+            ServiceOrderIDPK, InvoiceType, InvoiceName, InvoiceMakeout, 
+            InvoiceCompanyPhone, InvoiceCompanyID, InvoiceCompanyAdd, 
+            InvoiceCompanyBank, InvoiceCompanyBankNo, InvoiceZipCode, 
+            Invoice_strAdd, Invoice_strName, Invoice_strPhone, Invoice_strEmail, 
+            ApplicationTime, AuditStatus, InvoiceMoney, ApplyOAID
+        ) VALUES (
+            #{ServiceOrderIDPK}, #{InvoiceType}, #{InvoiceName}, #{InvoiceMakeout}, 
+            #{InvoiceCompanyPhone}, #{InvoiceCompanyID}, #{InvoiceCompanyAdd}, 
+            #{InvoiceCompanyBank}, #{InvoiceCompanyBankNo}, #{InvoiceZipCode}, 
+            #{Invoice_strAdd}, #{Invoice_strName}, #{Invoice_strPhone}, #{Invoice_strEmail}, 
+            GETDATE(), 0, #{InvoiceMoney}, #{ApplyOAID}
+        )
+    </insert>
+
+    <select id="selectUpdatedInvoices" parameterType="String" resultType="Map">
+        SELECT 
+            InvoiceID, ServiceOrderIDPK, AuditStatus, AuditTime, AuditMakeout,
+            InvoiceNo, InvoiceURL, InvoiceOddNo, EleCloud_PDF, EleCloud_Time
+        FROM InvoiceData
+        WHERE AuditTime &gt; #{lastSyncTime} OR EleCloud_Time &gt; #{lastSyncTime}
+    </select>
+
+    <select id="selectLegacyInvoiceByServiceOrderId" parameterType="Long" resultType="Map">
+        SELECT * FROM InvoiceData WHERE ServiceOrderIDPK = #{serviceOrderId}
+    </select>
+</mapper>
diff --git a/ruoyi-system/src/main/resources/mapper/system/SysInvoiceMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysInvoiceMapper.xml
new file mode 100644
index 0000000..a885373
--- /dev/null
+++ b/ruoyi-system/src/main/resources/mapper/system/SysInvoiceMapper.xml
@@ -0,0 +1,302 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysInvoiceMapper">
+    
+    <resultMap type="SysInvoice" id="SysInvoiceResult">
+        <result property="invoiceId"    column="invoice_id"    />
+        <result property="serviceOrderId"    column="service_order_id"    />
+        <result property="legacyServiceOrderId"    column="legacy_service_order_id"    />
+        <result property="invoiceType"    column="invoice_type"    />
+        <result property="invoiceName"    column="invoice_name"    />
+        <result property="invoiceMoney"    column="invoice_money"    />
+        <result property="invoiceRemarks"    column="invoice_remarks"    />
+        <result property="companyAddress"    column="company_address"    />
+        <result property="companyBank"    column="company_bank"    />
+        <result property="companyBankNo"    column="company_bank_no"    />
+        <result property="zipCode"    column="zip_code"    />
+        <result property="mailAddress"    column="mail_address"    />
+        <result property="contactName"    column="contact_name"    />
+        <result property="contactPhone"    column="contact_phone"    />
+        <result property="contactEmail"    column="contact_email"    />
+        <result property="status"    column="status"    />
+        <result property="invoiceNo"    column="invoice_no"    />
+        <result property="invoiceUrl"    column="invoice_url"    />
+        <result property="applyUserId"    column="apply_user_id"    />
+        <result property="applyTime"    column="apply_time"    />
+        <result property="auditUserId"    column="audit_user_id"    />
+        <result property="auditTime"    column="audit_time"    />
+        <result property="auditRemarks"    column="audit_remarks"    />
+        <result property="syncStatus"    column="sync_status"    />
+        <result property="legacyInvoiceId"    column="legacy_invoice_id"    />
+        <result property="serviceCode"    column="serviceCode"    />
+    </resultMap>
+
+    <sql id="selectSysInvoiceVo">
+        select invoice_id, service_order_id, legacy_service_order_id, invoice_type, invoice_name, invoice_money, invoice_remarks, company_address, company_bank, company_bank_no, zip_code, mail_address, contact_name, contact_phone, contact_email, status, invoice_no, invoice_url, apply_user_id, apply_time, audit_user_id, audit_time, audit_remarks, sync_status, legacy_invoice_id from sys_invoice
+    </sql>
+
+    <select id="selectSysInvoiceList" parameterType="SysInvoice" resultMap="SysInvoiceResult">
+        select i.invoice_id, i.service_order_id, i.legacy_service_order_id, i.invoice_type, i.invoice_name, 
+               i.invoice_money, i.invoice_remarks, i.company_address, i.company_bank, i.company_bank_no, 
+               i.zip_code, i.mail_address, i.contact_name, i.contact_phone, i.contact_email, 
+               i.status, i.invoice_no, i.invoice_url, i.apply_user_id, i.apply_time, 
+               i.audit_user_id, i.audit_time, i.audit_remarks, i.sync_status, i.legacy_invoice_id,
+               CONCAT(
+                   IFNULL(e.legacy_service_ord_class, ''),
+                   DATE_FORMAT(e.legacy_service_ns_time, '%Y%m%d'),
+                   '-',
+                   LPAD(IFNULL(e.legacy_service_ord_no, ''), 3, '0')
+               ) as serviceCode
+        from sys_invoice i
+        LEFT JOIN sys_task_emergency e ON i.service_order_id = e.task_id
+        <where>  
+            <if test="serviceOrderId != null "> and i.service_order_id = #{serviceOrderId}</if>
+            <if test="legacyServiceOrderId != null "> and i.legacy_service_order_id = #{legacyServiceOrderId}</if>
+            <if test="invoiceType != null "> and i.invoice_type = #{invoiceType}</if>
+            <if test="invoiceName != null  and invoiceName != ''"> and i.invoice_name like concat('%', #{invoiceName}, '%')</if>
+            <if test="status != null "> and i.status = #{status}</if>
+            <!-- serviceCode鏌ヨ -->
+            <if test="params.serviceCode != null and params.serviceCode != ''">
+                AND CONCAT(
+                    IFNULL(e.legacy_service_ord_class, ''),
+                    DATE_FORMAT(e.legacy_service_ns_time, '%Y%m%d'),
+                    '-',
+                    LPAD(IFNULL(e.legacy_service_ord_no, ''), 3, '0')
+                ) LIKE CONCAT('%', #{params.serviceCode}, '%')
+            </if>
+            <if test="params.beginTime != null and params.beginTime != ''"><!-- 寮�濮嬫椂闂存绱� -->
+                AND date_format(i.apply_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
+            </if>
+            <if test="params.endTime != null and params.endTime != ''"><!-- 缁撴潫鏃堕棿妫�绱� -->
+                AND date_format(i.apply_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
+            </if>
+            <!-- 鍒嗗叕鍙告煡璇㈤�昏緫闇�瑕佸叧鑱旀湇鍔″崟琛� -->
+            <if test="params.serviceOrdClass != null and params.serviceOrdClass != ''">
+                AND i.service_order_id IN (SELECT ServiceOrdID FROM service_order WHERE ServiceOrdClass = #{params.serviceOrdClass})
+            </if>
+        </where>
+    </select>
+    
+    <!-- App绔煡璇㈡垜鐨勫彂绁ㄥ垪琛紝杩斿洖Map鍖呭惈serviceCode -->
+    <select id="selectMyInvoiceList" parameterType="SysInvoice" resultType="map">
+        SELECT 
+            i.invoice_id as invoiceId,
+            i.service_order_id as serviceOrderId,
+            i.legacy_service_order_id as legacyServiceOrderId,
+            i.invoice_type as invoiceType,
+            i.invoice_name as invoiceName,
+            i.invoice_money as invoiceMoney,
+            i.invoice_remarks as invoiceRemarks,
+            i.company_address as companyAddress,
+            i.company_bank as companyBank,
+            i.company_bank_no as companyBankNo,
+            i.zip_code as zipCode,
+            i.mail_address as mailAddress,
+            i.contact_name as contactName,
+            i.contact_phone as contactPhone,
+            i.contact_email as contactEmail,
+            i.status,
+            i.invoice_no as invoiceNo,
+            i.invoice_url as invoiceUrl,
+            i.apply_user_id as applyUserId,
+            i.apply_time as applyTime,
+            i.audit_user_id as auditUserId,
+            i.audit_time as auditTime,
+            i.audit_remarks as auditRemarks,
+            i.sync_status as syncStatus,
+            i.legacy_invoice_id as legacyInvoiceId,
+            -- 鏋勫缓 serviceCode
+            CONCAT(
+                IFNULL(e.legacy_service_ord_class, ''),
+                DATE_FORMAT(e.legacy_service_ns_time, '%Y%m%d'),
+                '-',
+                LPAD(IFNULL(e.legacy_service_ord_no, ''), 3, '0')
+            ) as serviceCode
+        FROM sys_invoice i
+        LEFT JOIN sys_task_emergency e ON i.service_order_id = e.task_id
+        <where>  
+            <if test="serviceOrderId != null "> and i.service_order_id = #{serviceOrderId}</if>
+            <if test="legacyServiceOrderId != null "> and i.legacy_service_order_id = #{legacyServiceOrderId}</if>
+            <if test="invoiceType != null "> and i.invoice_type = #{invoiceType}</if>
+            <if test="invoiceName != null  and invoiceName != ''"> and i.invoice_name like concat('%', #{invoiceName}, '%')</if>
+            <if test="status != null "> and i.status = #{status}</if>
+            <!-- 鏈嶅姟鍗曞彿鎼滅储 -->
+            <if test="params.serviceCode != null and params.serviceCode != ''">
+                AND CONCAT(
+                    IFNULL(e.legacy_service_ord_class, ''),
+                    DATE_FORMAT(e.legacy_service_ns_time, '%Y%m%d'),
+                    '-',
+                    IFNULL(e.legacy_service_ord_no, '')
+                ) LIKE CONCAT('%', #{params.serviceCode}, '%')
+            </if>
+            <if test="params.beginTime != null and params.beginTime != ''"><!-- 寮�濮嬫椂闂存绱� -->
+                AND date_format(i.apply_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
+            </if>
+            <if test="params.endTime != null and params.endTime != ''"><!-- 缁撴潫鏃堕棿妫�绱� -->
+                AND date_format(i.apply_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
+            </if>
+            <!-- 鍒嗗叕鍙告煡璇㈤�昏緫闇�瑕佸叧鑱旀湇鍔″崟琛� -->
+            <if test="params.serviceOrdClass != null and params.serviceOrdClass != ''">
+                AND i.service_order_id IN (SELECT ServiceOrdID FROM service_order WHERE ServiceOrdClass = #{params.serviceOrdClass})
+            </if>
+        </where>
+    </select>
+    
+    <select id="selectSysInvoiceByInvoiceId" parameterType="Long" resultMap="SysInvoiceResult">
+        <include refid="selectSysInvoiceVo"/>
+        where invoice_id = #{invoiceId}
+    </select>
+
+    <select id="selectSysInvoiceByLegacyId" parameterType="Integer" resultMap="SysInvoiceResult">
+        <include refid="selectSysInvoiceVo"/>
+        where legacy_invoice_id = #{legacyInvoiceId}
+    </select>
+
+    <select id="selectSelectableTasks" resultType="Map">
+        SELECT 
+            t.task_id as taskId, 
+            t.task_code as taskCode, 
+            e.legacy_service_ord_id as legacyServiceOrderId,
+            t.actual_end_time as completionTime,
+            t.departure_address as departure,
+            t.destination_address as destination,
+            e.legacy_service_ord_class as legacyServiceOrdClass,
+            e.legacy_service_ns_time as legacyServiceNsTime,
+            e.legacy_service_ord_no as legacyServiceOrdNo,
+            IFNULL(e.transfer_price, 0) as transferPrice,
+            CONCAT(
+                IFNULL(e.legacy_service_ord_class, ''),
+                DATE_FORMAT(e.legacy_service_ns_time, '%Y%m%d'),
+                '-',
+                LPAD(IFNULL(e.legacy_service_ord_no, ''), 3, '0')
+            ) as serviceCode
+        FROM sys_task t
+        INNER JOIN sys_task_emergency e ON t.task_id = e.task_id
+        WHERE t.task_type = 'EMERGENCY_TRANSFER' 
+          AND t.task_status = 'COMPLETED'
+          AND (t.creator_id = #{userId} OR t.assignee_id = #{userId})
+          AND t.task_id NOT IN (
+              SELECT service_order_id 
+              FROM sys_invoice 
+              WHERE status IN (0, 1)
+          )
+          <if test="searchKeyword != null and searchKeyword != ''">
+              AND (
+                  t.task_code LIKE CONCAT('%', #{searchKeyword}, '%')
+                  OR e.legacy_service_ord_no LIKE CONCAT('%', #{searchKeyword}, '%')
+                  OR CONCAT(
+                      IFNULL(e.legacy_service_ord_class, ''),
+                      DATE_FORMAT(e.legacy_service_ns_time, '%Y%m%d'),
+                      '-',
+                    LPAD(IFNULL(e.legacy_service_ord_no, ''), 3, '0')
+
+                  ) LIKE CONCAT('%', #{searchKeyword}, '%')
+              )
+          </if>
+          <if test="serviceOrdClass != null and serviceOrdClass != ''">
+              AND e.legacy_service_ord_class = #{serviceOrdClass}
+          </if>
+        ORDER BY t.actual_end_time DESC
+        LIMIT 100
+    </select>
+        
+    <insert id="insertSysInvoice" parameterType="SysInvoice" useGeneratedKeys="true" keyProperty="invoiceId">
+        insert into sys_invoice
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="serviceOrderId != null">service_order_id,</if>
+            <if test="legacyServiceOrderId != null">legacy_service_order_id,</if>
+            <if test="invoiceType != null">invoice_type,</if>
+            <if test="invoiceName != null and invoiceName != ''">invoice_name,</if>
+            <if test="invoiceMoney != null">invoice_money,</if>
+            <if test="invoiceRemarks != null">invoice_remarks,</if>
+            <if test="companyAddress != null">company_address,</if>
+            <if test="companyBank != null">company_bank,</if>
+            <if test="companyBankNo != null">company_bank_no,</if>
+            <if test="zipCode != null">zip_code,</if>
+            <if test="mailAddress != null">mail_address,</if>
+            <if test="contactName != null">contact_name,</if>
+            <if test="contactPhone != null">contact_phone,</if>
+            <if test="contactEmail != null">contact_email,</if>
+            <if test="status != null">status,</if>
+            <if test="invoiceNo != null">invoice_no,</if>
+            <if test="invoiceUrl != null">invoice_url,</if>
+            <if test="applyUserId != null">apply_user_id,</if>
+            <if test="applyTime != null">apply_time,</if>
+            <if test="auditUserId != null">audit_user_id,</if>
+            <if test="auditTime != null">audit_time,</if>
+            <if test="auditRemarks != null">audit_remarks,</if>
+            <if test="syncStatus != null">sync_status,</if>
+            <if test="legacyInvoiceId != null">legacy_invoice_id,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="serviceOrderId != null">#{serviceOrderId},</if>
+            <if test="legacyServiceOrderId != null">#{legacyServiceOrderId},</if>
+            <if test="invoiceType != null">#{invoiceType},</if>
+            <if test="invoiceName != null and invoiceName != ''">#{invoiceName},</if>
+            <if test="invoiceMoney != null">#{invoiceMoney},</if>
+            <if test="invoiceRemarks != null">#{invoiceRemarks},</if>
+            <if test="companyAddress != null">#{companyAddress},</if>
+            <if test="companyBank != null">#{companyBank},</if>
+            <if test="companyBankNo != null">#{companyBankNo},</if>
+            <if test="zipCode != null">#{zipCode},</if>
+            <if test="mailAddress != null">#{mailAddress},</if>
+            <if test="contactName != null">#{contactName},</if>
+            <if test="contactPhone != null">#{contactPhone},</if>
+            <if test="contactEmail != null">#{contactEmail},</if>
+            <if test="status != null">#{status},</if>
+            <if test="invoiceNo != null">#{invoiceNo},</if>
+            <if test="invoiceUrl != null">#{invoiceUrl},</if>
+            <if test="applyUserId != null">#{applyUserId},</if>
+            <if test="applyTime != null">#{applyTime},</if>
+            <if test="auditUserId != null">#{auditUserId},</if>
+            <if test="auditTime != null">#{auditTime},</if>
+            <if test="auditRemarks != null">#{auditRemarks},</if>
+            <if test="syncStatus != null">#{syncStatus},</if>
+            <if test="legacyInvoiceId != null">#{legacyInvoiceId},</if>
+         </trim>
+    </insert>
+
+    <update id="updateSysInvoice" parameterType="SysInvoice">
+        update sys_invoice
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="serviceOrderId != null">service_order_id = #{serviceOrderId},</if>
+            <if test="legacyServiceOrderId != null">legacy_service_order_id = #{legacyServiceOrderId},</if>
+            <if test="invoiceType != null">invoice_type = #{invoiceType},</if>
+            <if test="invoiceName != null and invoiceName != ''">invoice_name = #{invoiceName},</if>
+            <if test="invoiceMoney != null">invoice_money = #{invoiceMoney},</if>
+            <if test="invoiceRemarks != null">invoice_remarks = #{invoiceRemarks},</if>
+            <if test="companyAddress != null">company_address = #{companyAddress},</if>
+            <if test="companyBank != null">company_bank = #{companyBank},</if>
+            <if test="companyBankNo != null">company_bank_no = #{companyBankNo},</if>
+            <if test="zipCode != null">zip_code = #{zipCode},</if>
+            <if test="mailAddress != null">mail_address = #{mailAddress},</if>
+            <if test="contactName != null">contact_name = #{contactName},</if>
+            <if test="contactPhone != null">contact_phone = #{contactPhone},</if>
+            <if test="contactEmail != null">contact_email = #{contactEmail},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="invoiceNo != null">invoice_no = #{invoiceNo},</if>
+            <if test="invoiceUrl != null">invoice_url = #{invoiceUrl},</if>
+            <if test="applyUserId != null">apply_user_id = #{applyUserId},</if>
+            <if test="applyTime != null">apply_time = #{applyTime},</if>
+            <if test="auditUserId != null">audit_user_id = #{auditUserId},</if>
+            <if test="auditTime != null">audit_time = #{auditTime},</if>
+            <if test="auditRemarks != null">audit_remarks = #{auditRemarks},</if>
+            <if test="syncStatus != null">sync_status = #{syncStatus},</if>
+            <if test="legacyInvoiceId != null">legacy_invoice_id = #{legacyInvoiceId},</if>
+        </trim>
+        where invoice_id = #{invoiceId}
+    </update>
+
+    <delete id="deleteSysInvoiceByInvoiceId" parameterType="Long">
+        delete from sys_invoice where invoice_id = #{invoiceId}
+    </delete>
+
+    <delete id="deleteSysInvoiceByInvoiceIds" parameterType="String">
+        delete from sys_invoice where invoice_id in 
+        <foreach item="invoiceId" collection="array" open="(" separator="," close=")">
+            #{invoiceId}
+        </foreach>
+    </delete>
+</mapper>
diff --git a/ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml b/ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml
index 73671e3..c56d5d6 100644
--- a/ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml
@@ -79,7 +79,7 @@
                segment_distance, gps_point_count, gps_ids, task_id, task_code, calculate_method
         FROM tb_vehicle_gps_segment_mileage
         WHERE vehicle_id = #{vehicleId}
-          AND segment_start_time &lt;= #{endTime}
+          AND segment_start_time between #{startDate} and #{endDate}
           AND segment_end_time &gt;= #{startTime}
           AND segment_distance &gt; 0
         ORDER BY segment_start_time
diff --git a/ruoyi-ui/src/api/system/dept.js b/ruoyi-ui/src/api/system/dept.js
index 7817d19..71aef07 100644
--- a/ruoyi-ui/src/api/system/dept.js
+++ b/ruoyi-ui/src/api/system/dept.js
@@ -59,6 +59,14 @@
   })
 }
 
+// 鎸塐A璁㈠崟绫诲埆鏌ヨ鍒嗗叕鍙稿垪琛紙鍒悕锛�
+export function listBranchByOaOrderClass() {
+  return request({
+    url: '/system/dept/branch/by-oa',
+    method: 'get'
+  })
+}
+
 // 鎸夊閮ㄤ紶鍏ョ殑鐢ㄦ埛ID杩斿洖鍏跺彲绠$悊鍒嗗叕鍙稿垪琛�
 export function listBranchByUser(userId) {
   return request({
diff --git a/ruoyi-ui/src/api/system/invoice.js b/ruoyi-ui/src/api/system/invoice.js
new file mode 100644
index 0000000..dd4c35e
--- /dev/null
+++ b/ruoyi-ui/src/api/system/invoice.js
@@ -0,0 +1,60 @@
+import request from '@/utils/request'
+
+// 鏌ヨ鍙戠エ鐢宠鍒楄〃
+export function listInvoice(query) {
+  return request({
+    url: '/system/invoice/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鏌ヨ鍙戠エ鐢宠璇︾粏
+export function getInvoice(invoiceId) {
+  return request({
+    url: '/system/invoice/' + invoiceId,
+    method: 'get'
+  })
+}
+
+// 鏂板鍙戠エ鐢宠
+export function addInvoice(data) {
+  return request({
+    url: '/system/invoice',
+    method: 'post',
+    data: data
+  })
+}
+
+// 淇敼鍙戠エ鐢宠
+export function updateInvoice(data) {
+  return request({
+    url: '/system/invoice',
+    method: 'put',
+    data: data
+  })
+}
+
+// 鍒犻櫎鍙戠エ鐢宠
+export function delInvoice(invoiceId) {
+  return request({
+    url: '/system/invoice/' + invoiceId,
+    method: 'delete'
+  })
+}
+
+// 鎵嬪姩鍚屾鐘舵��
+export function syncInvoiceStatus() {
+  return request({
+    url: '/system/invoice/syncStatus',
+    method: 'get'
+  })
+}
+
+// 鍚屾鍗曚釜鍙戠エ鍒版棫绯荤粺
+export function syncInvoiceToLegacy(invoiceId) {
+  return request({
+    url: '/system/invoice/syncToLegacy/' + invoiceId,
+    method: 'post'
+  })
+}
diff --git a/ruoyi-ui/src/api/task.js b/ruoyi-ui/src/api/task.js
index b72da8d..1bb98c6 100644
--- a/ruoyi-ui/src/api/task.js
+++ b/ruoyi-ui/src/api/task.js
@@ -302,4 +302,12 @@
     url: '/task/syncDispatchOrder/' + taskId,
     method: 'post'
   })
+}
+
+// 鎵嬪姩鍚屾浠诲姟鐘舵�佸埌鏃х郴缁�
+export function syncTaskStatus(taskId) {
+  return request({
+    url: '/task/syncTaskStatus/' + taskId,
+    method: 'post'
+  })
 }
\ No newline at end of file
diff --git a/ruoyi-ui/src/router/index.js b/ruoyi-ui/src/router/index.js
index 6266b3e..59f7fee 100644
--- a/ruoyi-ui/src/router/index.js
+++ b/ruoyi-ui/src/router/index.js
@@ -145,6 +145,27 @@
     hidden: false,
     name: 'H5TaskCreate',
     meta: { title: '鍒涘缓浠诲姟' }
+  },
+  {
+    path: '/system/invoice/detail',
+    component: () => import('@/views/system/invoice/detail'),
+    name: 'InvoiceDetail',
+    hidden: true,
+    meta: { title: '鍙戠エ璇︽儏', activeMenu: '/system/invoice' }
+  },
+  {
+    path: '/system/invoice/audit',
+    component: () => import('@/views/system/invoice/audit'),
+    name: 'InvoiceAudit',
+    hidden: true,
+    meta: { title: '瀹℃牳鍙戠エ', activeMenu: '/system/invoice' }
+  },
+  {
+    path: '/system/invoice/apply',
+    component: () => import('@/views/system/invoice/apply'),
+    name: 'InvoiceApply',
+    hidden: true,
+    meta: { title: '鐢宠鍙戠エ', activeMenu: '/system/invoice' }
   }
 ]
 
diff --git a/ruoyi-ui/src/views/system/invoice/apply.vue b/ruoyi-ui/src/views/system/invoice/apply.vue
new file mode 100644
index 0000000..06ed00c
--- /dev/null
+++ b/ruoyi-ui/src/views/system/invoice/apply.vue
@@ -0,0 +1,261 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-card">
+      <div slot="header" class="clearfix">
+        <span class="card-title">鍙戠エ鐢宠</span>
+        <el-button style="float: right; padding: 3px 0" type="text" @click="goBack">杩斿洖</el-button>
+      </div>
+
+      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+        <!-- 浠诲姟淇℃伅灞曠ず -->
+        <el-divider>浠诲姟淇℃伅</el-divider>
+        <el-descriptions :column="2" border v-if="taskInfo.taskId">
+          <el-descriptions-item label="浠诲姟缂栧彿">{{ taskInfo.taskCode }}</el-descriptions-item>
+          <el-descriptions-item label="鏈嶅姟鍗曞彿">{{ taskInfo.serviceCode || taskInfo.legacyServiceOrderId || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="鍑哄彂鍦�" :span="2">{{ taskInfo.departure }}</el-descriptions-item>
+          <el-descriptions-item label="鐩殑鍦�" :span="2">{{ taskInfo.destination }}</el-descriptions-item>
+          <el-descriptions-item label="瀹屾垚鏃堕棿">{{ taskInfo.completionTime }}</el-descriptions-item>
+          <el-descriptions-item label="浠诲姟閲戦">
+            <span style="color: #F56C6C; font-weight: bold;">楼{{ taskInfo.transferPrice || '0.00' }}</span>
+          </el-descriptions-item>
+        </el-descriptions>
+
+        <!-- 鍙戠エ淇℃伅 -->
+        <el-divider>鍙戠エ淇℃伅</el-divider>
+        
+        <el-form-item label="寮�绁ㄧ被鍨�" prop="invoiceType">
+          <el-radio-group v-model="form.invoiceType">
+            <el-radio :label="1">涓汉</el-radio>
+            <el-radio :label="2">浼佷笟</el-radio>
+          </el-radio-group>
+        </el-form-item>
+
+        <el-form-item label="鍙戠エ鎶ご" prop="invoiceName">
+          <el-input v-model="form.invoiceName" placeholder="璇疯緭鍏ュ彂绁ㄦ姮澶�" maxlength="200" />
+        </el-form-item>
+
+        <el-form-item label="鍙戠エ閲戦" prop="invoiceMoney">
+          <el-input 
+            v-model="form.invoiceMoney" 
+            placeholder="璇疯緭鍏ュ彂绁ㄩ噾棰�" 
+            type="number"
+            :max="maxInvoiceMoney"
+          >
+            <template slot="append">鍏�</template>
+          </el-input>
+          <div v-if="maxInvoiceMoney" style="color: #909399; font-size: 12px; margin-top: 4px;">
+            鍙敵璇烽噾棰濅笂闄愶細楼{{ maxInvoiceMoney }}
+          </div>
+        </el-form-item>
+
+        <el-form-item label="鍙戠エ澶囨敞">
+          <el-input v-model="form.invoiceRemarks" type="textarea" placeholder="璇疯緭鍏ュ娉�" maxlength="500" />
+        </el-form-item>
+
+        <!-- 浼佷笟淇℃伅锛堜粎浼佷笟绫诲瀷鏄剧ず锛� -->
+        <template v-if="form.invoiceType === 2">
+          <el-divider>浼佷笟淇℃伅</el-divider>
+          
+          <el-form-item label="娉ㄥ唽鍦板潃" prop="companyAddress">
+            <el-input v-model="form.companyAddress" placeholder="璇疯緭鍏ヤ紒涓氭敞鍐屽湴鍧�" maxlength="200" />
+          </el-form-item>
+
+          <el-form-item label="寮�鎴烽摱琛�" prop="companyBank">
+            <el-input v-model="form.companyBank" placeholder="璇疯緭鍏ュ紑鎴烽摱琛�" maxlength="100" />
+          </el-form-item>
+
+          <el-form-item label="閾惰璐﹀彿" prop="companyBankNo">
+            <el-input v-model="form.companyBankNo" placeholder="璇疯緭鍏ラ摱琛岃处鍙�" maxlength="50" />
+          </el-form-item>
+        </template>
+
+        <!-- 閭瘎淇℃伅 -->
+        <el-divider>閭瘎淇℃伅</el-divider>
+
+        <el-form-item label="閭瘎鍦板潃" prop="mailAddress">
+          <el-input v-model="form.mailAddress" placeholder="璇疯緭鍏ラ偖瀵勫湴鍧�" maxlength="200" />
+        </el-form-item>
+
+        <el-form-item label="閭紪">
+          <el-input v-model="form.zipCode" placeholder="璇疯緭鍏ラ偖缂�" maxlength="10" />
+        </el-form-item>
+
+        <el-form-item label="鑱旂郴浜�" prop="contactName">
+          <el-input v-model="form.contactName" placeholder="璇疯緭鍏ヨ仈绯讳汉" maxlength="50" />
+        </el-form-item>
+
+        <el-form-item label="鑱旂郴鐢佃瘽" prop="contactPhone">
+          <el-input v-model="form.contactPhone" placeholder="璇疯緭鍏ヨ仈绯荤數璇�" maxlength="20" />
+        </el-form-item>
+
+        <el-form-item label="鑱旂郴閭">
+          <el-input v-model="form.contactEmail" placeholder="璇疯緭鍏ヨ仈绯婚偖绠�" maxlength="100" />
+        </el-form-item>
+      </el-form>
+
+      <div class="action-bar" style="margin-top: 20px; text-align: center;">
+        <el-button @click="goBack">鍙栨秷</el-button>
+        <el-button type="primary" @click="submitForm">鎻愪氦鐢宠</el-button>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import { addInvoice } from "@/api/system/invoice";
+
+export default {
+  name: "InvoiceApply",
+  data() {
+    return {
+      // 浠诲姟淇℃伅
+      taskInfo: {
+        taskId: null,
+        taskCode: null,
+        legacyServiceOrderId: null,
+        serviceCode: null,
+        departure: null,
+        destination: null,
+        completionTime: null,
+        transferPrice: null
+      },
+      // 琛ㄥ崟鏁版嵁
+      form: {
+        serviceOrderId: null,
+        legacyServiceOrderId: null,  // 鏃х郴缁熸湇鍔″崟ID
+        invoiceType: 1,
+        invoiceName: null,
+        invoiceMoney: null,
+        invoiceRemarks: null,
+        companyAddress: null,
+        companyBank: null,
+        companyBankNo: null,
+        zipCode: null,
+        mailAddress: null,
+        contactName: null,
+        contactPhone: null,
+        contactEmail: null
+      },
+      // 鏈�澶у彂绁ㄩ噾棰�
+      maxInvoiceMoney: null,
+      // 琛ㄥ崟鏍¢獙
+      rules: {
+        invoiceType: [
+          { required: true, message: "璇烽�夋嫨寮�绁ㄧ被鍨�", trigger: "change" }
+        ],
+        invoiceName: [
+          { required: true, message: "璇疯緭鍏ュ彂绁ㄦ姮澶�", trigger: "blur" }
+        ],
+        invoiceMoney: [
+          { required: true, message: "璇疯緭鍏ュ彂绁ㄩ噾棰�", trigger: "blur" },
+          { 
+            validator: (rule, value, callback) => {
+              if (!value || value <= 0) {
+                callback(new Error('鍙戠エ閲戦蹇呴』澶т簬0'));
+              } else if (this.maxInvoiceMoney && parseFloat(value) > this.maxInvoiceMoney) {
+                callback(new Error(`鍙戠エ閲戦涓嶈兘瓒呰繃浠诲姟閲戦楼${this.maxInvoiceMoney}`));
+              } else {
+                callback();
+              }
+            }, 
+            trigger: 'blur' 
+          }
+        ],
+        companyAddress: [
+          { required: true, message: "璇疯緭鍏ヤ紒涓氭敞鍐屽湴鍧�", trigger: "blur" }
+        ],
+        companyBank: [
+          { required: true, message: "璇疯緭鍏ュ紑鎴烽摱琛�", trigger: "blur" }
+        ],
+        companyBankNo: [
+          { required: true, message: "璇疯緭鍏ラ摱琛岃处鍙�", trigger: "blur" }
+        ],
+        mailAddress: [
+          { required: true, message: "璇疯緭鍏ラ偖瀵勫湴鍧�", trigger: "blur" }
+        ],
+        contactName: [
+          { required: true, message: "璇疯緭鍏ヨ仈绯讳汉", trigger: "blur" }
+        ],
+        contactPhone: [
+          { required: true, message: "璇疯緭鍏ヨ仈绯荤數璇�", trigger: "blur" },
+          { pattern: /^1[3-9]\d{9}$/, message: "璇疯緭鍏ユ纭殑鎵嬫満鍙风爜", trigger: "blur" }
+        ]
+      }
+    };
+  },
+  created() {
+    // 浠� sessionStorage 鑾峰彇浠诲姟淇℃伅
+    const taskInfoStr = sessionStorage.getItem('invoiceTaskInfo');
+    if (taskInfoStr) {
+      this.taskInfo = JSON.parse(taskInfoStr);
+      this.form.serviceOrderId = this.taskInfo.taskId;
+      // 濡傛灉浠诲姟淇℃伅涓湁鏃х郴缁熸湇鍔″崟ID锛屼篃瑕佷繚瀛�
+      if (this.taskInfo.legacyServiceOrderId) {
+        this.form.legacyServiceOrderId = this.taskInfo.legacyServiceOrderId;
+      }
+      
+      // 璁剧疆鏈�澶у彂绁ㄩ噾棰�
+      if (this.taskInfo.transferPrice) {
+        this.maxInvoiceMoney = parseFloat(this.taskInfo.transferPrice);
+        // 鑷姩甯﹀叆浠诲姟閲戦
+        this.form.invoiceMoney = this.maxInvoiceMoney;
+      }
+      
+      // 娓呴櫎 sessionStorage
+      sessionStorage.removeItem('invoiceTaskInfo');
+    } else if (this.$route.query.taskId) {
+      // 濡傛灉娌℃湁 sessionStorage锛屽皾璇曚粠 query 鑾峰彇
+      this.$modal.msgError("缂哄皯浠诲姟淇℃伅锛岃浠庝换鍔¤鎯呴〉杩涘叆");
+      this.goBack();
+    }
+  },
+  methods: {
+    /** 鎻愪氦琛ㄥ崟 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          // 浼佷笟绫诲瀷闇�瑕侀獙璇佷紒涓氫俊鎭�
+          if (this.form.invoiceType === 2) {
+            if (!this.form.companyAddress || !this.form.companyBank || !this.form.companyBankNo) {
+              this.$modal.msgError("璇峰畬鍠勪紒涓氫俊鎭�");
+              return;
+            }
+          }
+          
+          // 鍐嶆楠岃瘉閲戦
+          const money = parseFloat(this.form.invoiceMoney);
+          if (this.maxInvoiceMoney && money > this.maxInvoiceMoney) {
+            this.$modal.msgError(`鍙戠エ閲戦涓嶈兘瓒呰繃浠诲姟閲戦楼${this.maxInvoiceMoney}`);
+            return;
+          }
+          
+          addInvoice(this.form).then(response => {
+            this.$modal.msgSuccess("鎻愪氦鎴愬姛");
+            setTimeout(() => {
+              this.goBack();
+            }, 1500);
+          });
+        }
+      });
+    },
+    
+    /** 杩斿洖 */
+    goBack() {
+      this.$tab.closePage();
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.card-title {
+  font-size: 18px;
+  font-weight: bold;
+  color: #303133;
+}
+
+.action-bar {
+  border-top: 1px solid #EBEEF5;
+  padding-top: 20px;
+}
+</style>
diff --git a/ruoyi-ui/src/views/system/invoice/audit.vue b/ruoyi-ui/src/views/system/invoice/audit.vue
new file mode 100644
index 0000000..b8d47b3
--- /dev/null
+++ b/ruoyi-ui/src/views/system/invoice/audit.vue
@@ -0,0 +1,311 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-card">
+      <div slot="header" class="clearfix">
+        <span class="card-title">瀹℃牳鍙戠エ鐢宠</span>
+        <el-button style="float: right; padding: 3px 0" type="text" @click="goBack">杩斿洖</el-button>
+      </div>
+
+      <!-- 鐢宠淇℃伅锛堝彧璇伙級 -->
+      <el-form ref="viewForm" :model="invoice" label-width="120px" disabled>
+        <el-divider content-position="left">鐢宠淇℃伅</el-divider>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鏈嶅姟鍗曞彿">
+              <el-input v-model="invoice.serviceCode" placeholder="鏆傛棤" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="寮�绁ㄧ被鍨�">
+              <el-radio-group v-model="invoice.invoiceType">
+                <el-radio :label="1">涓汉</el-radio>
+                <el-radio :label="2">浼佷笟</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-form-item label="鍙戠エ鎶ご">
+              <el-input v-model="invoice.invoiceName" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鍙戠エ閲戦">
+              <el-input v-model="invoice.invoiceMoney">
+                <template slot="prepend">楼</template>
+              </el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鑱旂郴鐢佃瘽">
+              <el-input v-model="invoice.contactPhone" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20" v-if="invoice.invoiceType === 2">
+          <el-col :span="24">
+            <el-form-item label="娉ㄥ唽鍦板潃">
+              <el-input v-model="invoice.companyAddress" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20" v-if="invoice.invoiceType === 2">
+          <el-col :span="12">
+            <el-form-item label="寮�鎴烽摱琛�">
+              <el-input v-model="invoice.companyBank" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="閾惰璐﹀彿">
+              <el-input v-model="invoice.companyBankNo" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-form-item label="閭瘎鍦板潃">
+              <el-input v-model="invoice.mailAddress" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-form-item label="澶囨敞">
+              <el-input type="textarea" v-model="invoice.invoiceRemarks" :rows="3" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+
+      <!-- 瀹℃牳淇℃伅 -->
+      <el-form ref="auditForm" :model="auditForm" :rules="rules" label-width="120px">
+        <el-divider content-position="left">瀹℃牳淇℃伅</el-divider>
+        
+        <el-form-item label="瀹℃牳缁撴灉" prop="status">
+          <el-radio-group v-model="auditForm.status" @change="handleStatusChange">
+            <el-radio :label="1">閫氳繃</el-radio>
+            <el-radio :label="2">椹冲洖</el-radio>
+          </el-radio-group>
+        </el-form-item>
+
+        <!-- 瀹℃牳閫氳繃鏃朵笂浼犲彂绁� -->
+        <el-form-item 
+          v-if="auditForm.status === 1" 
+          label="鍙戠エ鏂囦欢" 
+          prop="invoiceUrl"
+          :rules="[{ required: true, message: '璇蜂笂浼犲彂绁ㄦ枃浠�', trigger: 'change' }]"
+        >
+          <el-upload
+            class="upload-demo"
+            :action="uploadAction"
+            :headers="uploadHeaders"
+            :on-success="handleUploadSuccess"
+            :on-error="handleUploadError"
+            :before-upload="beforeUpload"
+            :file-list="fileList"
+            :limit="1"
+            accept=".pdf,.jpg,.jpeg,.png"
+          >
+            <el-button size="small" type="primary">鐐瑰嚮涓婁紶</el-button>
+            <div slot="tip" class="el-upload__tip">鏀寔PDF銆丣PG銆丳NG鏍煎紡锛屼笖涓嶈秴杩�10MB</div>
+          </el-upload>
+          <div v-if="auditForm.invoiceUrl" style="margin-top: 10px;">
+            <el-link type="primary" :href="auditForm.invoiceUrl" target="_blank">鏌ョ湅宸蹭笂浼犲彂绁�</el-link>
+          </div>
+        </el-form-item>
+
+        <el-form-item v-if="auditForm.status === 1" label="鍙戠エ缂栧彿" prop="invoiceNo">
+          <el-input v-model="auditForm.invoiceNo" placeholder="璇疯緭鍏ュ彂绁ㄧ紪鍙�" />
+        </el-form-item>
+
+        <!-- 椹冲洖鏃跺繀濉┏鍥炲師鍥� -->
+        <el-form-item 
+          label="瀹℃牳澶囨敞" 
+          prop="auditRemarks"
+          :rules="auditForm.status === 2 ? [{ required: true, message: '璇疯緭鍏ラ┏鍥炲師鍥�', trigger: 'blur' }] : []"
+        >
+          <el-input 
+            type="textarea" 
+            v-model="auditForm.auditRemarks" 
+            :rows="4"
+            :placeholder="auditForm.status === 2 ? '璇疯緭鍏ラ┏鍥炲師鍥狅紙蹇呭~锛�' : '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'" 
+          />
+        </el-form-item>
+      </el-form>
+
+      <!-- 鎿嶄綔鎸夐挳 -->
+      <div class="action-bar">
+        <el-button @click="goBack">鍙� 娑�</el-button>
+        <el-button type="primary" @click="submitAudit" :loading="submitting">鎻愪氦瀹℃牳</el-button>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import { getInvoice, updateInvoice } from "@/api/system/invoice";
+import { getToken } from "@/utils/auth";
+import { Message } from 'element-ui';
+
+export default {
+  name: "InvoiceAudit",
+  data() {
+    return {
+      // 鍙戠エ淇℃伅
+      invoice: {},
+      // 瀹℃牳琛ㄥ崟
+      auditForm: {
+        invoiceId: null,
+        status: 1,
+        invoiceNo: null,
+        invoiceUrl: null,
+        auditRemarks: null
+      },
+      // 涓婁紶鐩稿叧
+      uploadAction: process.env.VUE_APP_BASE_API + "/common/upload",
+      uploadHeaders: { Authorization: "Bearer " + getToken() },
+      fileList: [],
+      // 鎻愪氦鐘舵��
+      submitting: false,
+      // 琛ㄥ崟楠岃瘉
+      rules: {
+        status: [{ required: true, message: "璇烽�夋嫨瀹℃牳缁撴灉", trigger: "change" }]
+      }
+    };
+  },
+  created() {
+    const invoiceId = this.$route.query.invoiceId;
+    if (invoiceId) {
+      this.getDetail(invoiceId);
+    } else {
+      this.$modal.msgError("缂哄皯鍙戠エID鍙傛暟");
+      this.goBack();
+    }
+  },
+  methods: {
+    /** 鑾峰彇鍙戠エ璇︽儏 */
+    getDetail(invoiceId) {
+      getInvoice(invoiceId).then(response => {
+        this.invoice = response.data;
+        this.auditForm.invoiceId = response.data.invoiceId;
+        
+        // 濡傛灉宸叉湁鍙戠エ鏂囦欢锛屾樉绀哄湪鏂囦欢鍒楄〃涓�
+        if (response.data.invoiceUrl) {
+          this.auditForm.invoiceUrl = response.data.invoiceUrl;
+          this.fileList = [{
+            name: '宸蹭笂浼犲彂绁�',
+            url: response.data.invoiceUrl
+          }];
+        }
+      }).catch(() => {
+        this.$modal.msgError("鑾峰彇鍙戠エ璇︽儏澶辫触");
+        this.goBack();
+      });
+    },
+
+    /** 鐘舵�佸彉鍖栧鐞� */
+    handleStatusChange(value) {
+      // 鍒囨崲鐘舵�佹椂娓呯┖瀹℃牳澶囨敞
+      this.auditForm.auditRemarks = '';
+    },
+
+    /** 鏂囦欢涓婁紶鍓嶉獙璇� */
+    beforeUpload(file) {
+      const isLt10M = file.size / 1024 / 1024 < 10;
+      if (!isLt10M) {
+        Message.error('涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 10MB!');
+        return false;
+      }
+      const fileType = file.type;
+      const isPDF = fileType === 'application/pdf';
+      const isImage = fileType.startsWith('image/');
+      if (!isPDF && !isImage) {
+        Message.error('鍙兘涓婁紶PDF鎴栧浘鐗囨枃浠�!');
+        return false;
+      }
+      return true;
+    },
+
+    /** 鏂囦欢涓婁紶鎴愬姛 */
+    handleUploadSuccess(response, file, fileList) {
+      if (response.code === 200) {
+        this.auditForm.invoiceUrl = response.url || response.fileName;
+        this.fileList = fileList;
+        Message.success('涓婁紶鎴愬姛');
+      } else {
+        Message.error(response.msg || '涓婁紶澶辫触');
+      }
+    },
+
+    /** 鏂囦欢涓婁紶澶辫触 */
+    handleUploadError(err, file, fileList) {
+      Message.error('涓婁紶澶辫触锛岃閲嶈瘯');
+      console.error(err);
+    },
+
+    /** 鎻愪氦瀹℃牳 */
+    submitAudit() {
+      this.$refs["auditForm"].validate(valid => {
+        if (valid) {
+          // 瀹℃牳閫氳繃鏃跺繀椤讳笂浼犲彂绁�
+          if (this.auditForm.status === 1 && !this.auditForm.invoiceUrl) {
+            this.$modal.msgError('瀹℃牳閫氳繃鏃跺繀椤讳笂浼犲彂绁ㄦ枃浠�');
+            return;
+          }
+          // 椹冲洖鏃跺繀椤诲~鍐欓┏鍥炲師鍥�
+          if (this.auditForm.status === 2 && !this.auditForm.auditRemarks) {
+            this.$modal.msgError('椹冲洖鏃跺繀椤诲~鍐欓┏鍥炲師鍥�');
+            return;
+          }
+
+          this.submitting = true;
+          updateInvoice(this.auditForm).then(response => {
+            this.$modal.msgSuccess("瀹℃牳鎴愬姛");
+            this.goBack();
+          }).catch(() => {
+            this.submitting = false;
+          });
+        }
+      });
+    },
+
+    /** 杩斿洖鍒楄〃 */
+    goBack() {
+      this.$tab.closePage();
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.card-title {
+  font-size: 18px;
+  font-weight: bold;
+  color: #303133;
+}
+
+.action-bar {
+  border-top: 1px solid #EBEEF5;
+  padding-top: 20px;
+  text-align: center;
+}
+
+::v-deep .el-form-item__label {
+  font-weight: 500;
+}
+
+::v-deep .el-divider__text {
+  font-size: 16px;
+  font-weight: bold;
+  color: #409EFF;
+}
+</style>
diff --git a/ruoyi-ui/src/views/system/invoice/detail.vue b/ruoyi-ui/src/views/system/invoice/detail.vue
new file mode 100644
index 0000000..f7ed8f3
--- /dev/null
+++ b/ruoyi-ui/src/views/system/invoice/detail.vue
@@ -0,0 +1,279 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-card">
+      <div slot="header" class="clearfix">
+        <span class="card-title">鍙戠エ鐢宠璇︽儏</span>
+        <el-button style="float: right; padding: 3px 0" type="text" @click="goBack">杩斿洖</el-button>
+      </div>
+
+      <el-descriptions :column="2" border>
+        <!-- 鍩烘湰淇℃伅 -->
+        <el-descriptions-item label="鍙戠エID">{{ invoice.invoiceId }}</el-descriptions-item>
+        <el-descriptions-item label="鏈嶅姟鍗曞彿">{{ invoice.serviceCode || invoice.legacyServiceOrderId || '-' }}</el-descriptions-item>
+        
+        <el-descriptions-item label="寮�绁ㄧ被鍨�">
+          <el-tag :type="invoice.invoiceType === 2 ? 'success' : 'info'" size="small">
+            {{ invoice.invoiceType === 2 ? '浼佷笟' : '涓汉' }}
+          </el-tag>
+        </el-descriptions-item>
+        
+        <el-descriptions-item label="鐢宠鐘舵��">
+          <el-tag :type="invoice.status === 1 ? 'success' : (invoice.status === 2 ? 'danger' : 'warning')" size="small">
+            {{ statusFormat(invoice.status) }}
+          </el-tag>
+        </el-descriptions-item>
+
+        <!-- 鍙戠エ淇℃伅 -->
+        <el-descriptions-item label="鍙戠エ鎶ご" :span="2">{{ invoice.invoiceName }}</el-descriptions-item>
+        <el-descriptions-item label="鍙戠エ閲戦">
+          <span class="text-price">楼{{ formatMoney(invoice.invoiceMoney) }}</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="鍙戠エ缂栧彿">{{ invoice.invoiceNo || '-' }}</el-descriptions-item>
+
+        <!-- 鑱旂郴淇℃伅 -->
+        <el-descriptions-item label="鑱旂郴浜�">{{ invoice.contactName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="鑱旂郴鐢佃瘽">{{ invoice.contactPhone || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="鑱旂郴閭" :span="2">{{ invoice.contactEmail || '-' }}</el-descriptions-item>
+
+        <!-- 浼佷笟淇℃伅锛堜粎浼佷笟绫诲瀷鏄剧ず锛� -->
+        <template v-if="invoice.invoiceType === 2">
+          <el-descriptions-item label="娉ㄥ唽鍦板潃" :span="2">{{ invoice.companyAddress || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="寮�鎴烽摱琛�">{{ invoice.companyBank || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="閾惰璐﹀彿">{{ invoice.companyBankNo || '-' }}</el-descriptions-item>
+        </template>
+
+        <!-- 閭瘎淇℃伅 -->
+        <el-descriptions-item label="閭瘎鍦板潃" :span="2">{{ invoice.mailAddress || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="閭紪">{{ invoice.zipCode || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="澶囨敞" :span="2">{{ invoice.invoiceRemarks || '-' }}</el-descriptions-item>
+
+        <!-- 鏃堕棿淇℃伅 -->
+        <el-descriptions-item label="鐢宠鏃堕棿">{{ parseTime(invoice.applyTime) }}</el-descriptions-item>
+        <el-descriptions-item label="瀹℃牳鏃堕棿">{{ parseTime(invoice.auditTime) || '-' }}</el-descriptions-item>
+        
+        <!-- 瀹℃牳淇℃伅 -->
+        <el-descriptions-item label="瀹℃牳澶囨敞" :span="2">
+          <span :class="invoice.status === 2 ? 'text-danger' : ''">{{ invoice.auditRemarks || '-' }}</span>
+        </el-descriptions-item>
+
+        <!-- 鍚屾鐘舵�� -->
+        <el-descriptions-item label="鍚屾鐘舵��">
+          <el-tag 
+            :type="invoice.syncStatus === 1 ? 'success' : (invoice.syncStatus === 2 ? 'danger' : 'info')" 
+            size="small"
+          >
+            {{ syncStatusFormat(invoice.syncStatus) }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="鏃х郴缁熷彂绁↖D">
+          <span v-if="invoice.legacyInvoiceId">{{ invoice.legacyInvoiceId }}</span>
+          <span v-else class="text-gray">鏈悓姝�</span>
+        </el-descriptions-item>
+
+        <!-- 鍙戠エ鏂囦欢 -->
+        <el-descriptions-item label="鍙戠エ鏂囦欢" :span="2" v-if="invoice.invoiceUrl">
+          <el-link type="primary" :href="getFullUrl(invoice.invoiceUrl)" target="_blank" icon="el-icon-view">
+            鏌ョ湅鍙戠エ鏂囦欢
+          </el-link>
+          <el-link type="success" :href="getFullUrl(invoice.invoiceUrl)" :download="getFileName(invoice.invoiceUrl)" icon="el-icon-download" style="margin-left: 10px;">
+            涓嬭浇鍙戠エ
+          </el-link>
+        </el-descriptions-item>
+      </el-descriptions>
+
+      <!-- 鎿嶄綔鎸夐挳 -->
+      <div class="action-bar" style="margin-top: 20px; text-align: center;">
+        <el-button @click="goBack">杩斿洖鍒楄〃</el-button>
+        <el-button 
+          type="primary" 
+          v-if="invoice.status === 0" 
+          @click="handleAudit"
+          v-hasPermi="['system:invoice:edit']"
+        >
+          瀹℃牳
+        </el-button>
+        <el-button 
+          type="success" 
+          v-if="invoice.invoiceUrl" 
+          @click="handleDownload"
+          icon="el-icon-download"
+        >
+          涓嬭浇鍙戠エ
+        </el-button>
+        <!-- 鍚屾鎸夐挳锛氬彧鏈夊凡閫氳繃涓旀湭鍚屾鎴栧悓姝ュけ璐ユ椂鏄剧ず -->
+        <el-button 
+          type="warning" 
+          v-if="invoice.status === 1 && (!invoice.legacyInvoiceId || invoice.syncStatus === 2)" 
+          @click="handleSync"
+          :loading="syncing"
+          icon="el-icon-refresh"
+          v-hasPermi="['system:invoice:edit']"
+        >
+          {{ syncing ? '鍚屾涓�...' : '鍚屾鍒版棫绯荤粺' }}
+        </el-button>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import { getInvoice, syncInvoiceToLegacy } from "@/api/system/invoice";
+
+export default {
+  name: "InvoiceDetail",
+  data() {
+    return {
+      // 鍙戠エ璇︽儏鏁版嵁
+      invoice: {
+        invoiceId: null,
+        serviceOrderId: null,
+        legacyServiceOrderId: null,
+        serviceCode: null,
+        invoiceType: null,
+        invoiceName: null,
+        invoiceMoney: null,
+        invoiceRemarks: null,
+        companyAddress: null,
+        companyBank: null,
+        companyBankNo: null,
+        zipCode: null,
+        mailAddress: null,
+        contactName: null,
+        contactPhone: null,
+        contactEmail: null,
+        status: null,
+        invoiceNo: null,
+        invoiceUrl: null,
+        applyTime: null,
+        auditTime: null,
+        auditRemarks: null,
+        syncStatus: null,
+        legacyInvoiceId: null
+      },
+      // 鍚屾鐘舵��
+      syncing: false
+    };
+  },
+  created() {
+    const invoiceId = this.$route.params.invoiceId || this.$route.query.invoiceId;
+    if (invoiceId) {
+      this.getDetail(invoiceId);
+    } else {
+      this.$modal.msgError("缂哄皯鍙戠エID鍙傛暟");
+      this.goBack();
+    }
+  },
+  methods: {
+    /** 鑾峰彇鍙戠エ璇︽儏 */
+    getDetail(invoiceId) {
+      getInvoice(invoiceId).then(response => {
+        this.invoice = response.data;
+      }).catch(() => {
+        this.$modal.msgError("鑾峰彇鍙戠エ璇︽儏澶辫触");
+        this.goBack();
+      });
+    },
+    
+    /** 鐘舵�佹牸寮忓寲 */
+    statusFormat(status) {
+      const map = { 0: "寰呭鏍�", 1: "宸查�氳繃", 2: "宸查┏鍥�" };
+      return map[status] || "鏈煡";
+    },
+    
+    /** 閲戦鏍煎紡鍖� */
+    formatMoney(money) {
+      if (money === null || money === undefined) return '0.00';
+      return Number(money).toFixed(2);
+    },
+    
+    /** 鍚屾鐘舵�佹牸寮忓寲 */
+    syncStatusFormat(status) {
+      const map = { 0: "鏈悓姝�", 1: "宸插悓姝�", 2: "鍚屾澶辫触" };
+      return map[status] || "鏈煡";
+    },
+    
+    /** 鑾峰彇瀹屾暣鐨勬枃浠禪RL */
+    getFullUrl(url) {
+      if (!url) return '';
+      if (url.startsWith('http')) {
+        return url;
+      }
+      return process.env.VUE_APP_BASE_API + url;
+    },
+    
+    /** 鑾峰彇鏂囦欢鍚� */
+    getFileName(url) {
+      if (!url) return '鍙戠エ鏂囦欢';
+      const parts = url.split('/');
+      return parts[parts.length - 1] || '鍙戠エ鏂囦欢';
+    },
+    
+    /** 杩斿洖鍒楄〃 */
+    goBack() {
+      this.$tab.closePage();
+    },
+    
+    /** 璺宠浆鍒板鏍搁〉闈� */
+    handleAudit() {
+      this.$router.push({
+        path: '/system/invoice/audit',
+        query: { invoiceId: this.invoice.invoiceId }
+      });
+    },
+    
+    /** 涓嬭浇鍙戠エ */
+    handleDownload() {
+      const url = this.getFullUrl(this.invoice.invoiceUrl);
+      window.open(url);
+    },
+    
+    /** 鍚屾鍒版棫绯荤粺 */
+    handleSync() {
+      this.$modal.confirm('纭灏嗚鍙戠エ鐢宠鍚屾鍒版棫绯荤粺鍚楋紵').then(() => {
+        this.syncing = true;
+        syncInvoiceToLegacy(this.invoice.invoiceId).then(response => {
+          this.$modal.msgSuccess('鍚屾鎴愬姛');
+          // 閲嶆柊鍔犺浇璇︽儏
+          this.getDetail(this.invoice.invoiceId);
+        }).catch(() => {
+          this.$modal.msgError('鍚屾澶辫触');
+        }).finally(() => {
+          this.syncing = false;
+        });
+      }).catch(() => {});
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.card-title {
+  font-size: 18px;
+  font-weight: bold;
+  color: #303133;
+}
+
+.text-price {
+  color: #F56C6C;
+  font-size: 16px;
+  font-weight: bold;
+}
+
+.text-danger {
+  color: #F56C6C;
+}
+
+.text-gray {
+  color: #909399;
+}
+
+.action-bar {
+  border-top: 1px solid #EBEEF5;
+  padding-top: 20px;
+}
+
+::v-deep .el-descriptions-item__label {
+  font-weight: bold;
+  width: 120px;
+}
+</style>
diff --git a/ruoyi-ui/src/views/system/invoice/index.vue b/ruoyi-ui/src/views/system/invoice/index.vue
new file mode 100644
index 0000000..7ba45ca
--- /dev/null
+++ b/ruoyi-ui/src/views/system/invoice/index.vue
@@ -0,0 +1,265 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
+      <el-form-item label="鏈嶅姟鍗曞彿" prop="serviceCode">
+        <el-input
+          v-model="queryParams.serviceCode"
+          placeholder="璇疯緭鍏ユ湇鍔″崟鍙凤紙濡侴Z202602锛�"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="鍒嗗叕鍙�" prop="serviceOrdClass">
+        <el-select v-model="queryParams.serviceOrdClass" placeholder="璇烽�夋嫨鍒嗗叕鍙�" clearable>
+          <el-option
+            v-for="dept in branchOptions"
+            :key="dept.serviceOrderClass"
+            :label="dept.deptName"
+            :value="dept.serviceOrderClass"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="寮�绁ㄦ姮澶�" prop="invoiceName">
+        <el-input
+          v-model="queryParams.invoiceName"
+          placeholder="璇疯緭鍏ュ彂绁ㄦ姮澶�"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="鐢宠鐘舵��" prop="status">
+        <el-select v-model="queryParams.status" placeholder="璇烽�夋嫨鐘舵��" clearable>
+          <el-option label="寰呭鏍�" :value="0" />
+          <el-option label="宸查�氳繃" :value="1" />
+          <el-option label="宸查┏鍥�" :value="2" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鐢宠鏃堕棿">
+        <el-date-picker
+          v-model="dateRange"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="寮�濮嬫棩鏈�"
+          end-placeholder="缁撴潫鏃ユ湡"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">鎼滅储</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['system:invoice:export']"
+        >瀵煎嚭</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="info"
+          plain
+          icon="el-icon-refresh"
+          size="mini"
+          @click="handleSync"
+          v-hasRole="['admin']"
+        >鍚屾鏃х郴缁熺姸鎬�</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="invoiceList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="ID" align="center" prop="invoiceId" width="80" />
+      <el-table-column label="鏈嶅姟鍗曞彿" align="center" prop="serviceCode" width="150" />
+      <el-table-column label="寮�绁ㄦ姮澶�" align="center" prop="invoiceName" width="150" />
+      <el-table-column label="閲戦" align="center" prop="invoiceMoney" width="100" />
+      <el-table-column label="绫诲瀷" align="center" prop="invoiceType">
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.invoiceType === 2 ? 'success' : 'info'">
+            {{ scope.row.invoiceType === 2 ? '浼佷笟' : '涓汉' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="鐘舵��" align="center" prop="status">
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.status === 1 ? 'success' : (scope.row.status === 2 ? 'danger' : 'warning')">
+            {{ statusFormat(scope.row.status) }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="鍙戠エ缂栧彿" align="center" prop="invoiceNo" />
+      <el-table-column label="鐢宠鏃堕棿" align="center" prop="applyTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.applyTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleView(scope.row)"
+          >璇︽儏</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['system:invoice:edit']"
+            v-if="scope.row.status === 0"
+          >瀹℃牳</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-download"
+            v-if="scope.row.invoiceUrl"
+            @click="handleDownload(scope.row)"
+          >涓嬭浇鍙戠エ</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script>
+import { listInvoice, syncInvoiceStatus } from "@/api/system/invoice";
+import { listBranchByOaOrderClass } from "@/api/system/dept";
+
+export default {
+  name: "Invoice",
+  data() {
+    return {
+      // 閬僵灞�
+      loading: true,
+      // 閫変腑鏁扮粍
+      ids: [],
+      // 闈炲崟涓鐢�
+      single: true,
+      // 闈炲涓鐢�
+      multiple: true,
+      // 鏄剧ず鎼滅储鏉′欢
+      showSearch: true,
+      // 鎬绘潯鏁�
+      total: 0,
+      // 鍙戠エ鐢宠琛ㄦ牸鏁版嵁
+      invoiceList: [],
+      // 鍒嗗叕鍙搁�夐」
+      branchOptions: [],
+      // 鏃ユ湡鑼冨洿
+      dateRange: [],
+      // 鏌ヨ鍙傛暟
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        serviceCode: null,
+        invoiceName: null,
+        status: null,
+        serviceOrdClass: null
+      }
+    };
+  },
+  created() {
+    this.getList();
+    this.getBranchList();
+  },
+  methods: {
+    /** 鏌ヨ鍒嗗叕鍙稿垪琛� */
+    getBranchList() {
+      listBranchByOaOrderClass().then(response => {
+        this.branchOptions = response.data;
+      });
+    },
+    /** 鏌ヨ鍙戠エ鐢宠鍒楄〃 */
+    getList() {
+      this.loading = true;
+      const params = this.addDateRange(this.queryParams, this.dateRange);
+      // 灏� serviceOrdClass 鏀惧叆 params 瀛楁涓紝瀵瑰簲鍚庡彴 XML 涓殑 params.serviceOrdClass
+      if (this.queryParams.serviceOrdClass) {
+        params['params[serviceOrdClass]'] = this.queryParams.serviceOrdClass;
+      }
+      // serviceCode 鏌ヨ
+      if (this.queryParams.serviceCode) {
+        params['params[serviceCode]'] = this.queryParams.serviceCode;
+      }
+      listInvoice(params).then(response => {
+        this.invoiceList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 澶氶�夋閫変腑鏁版嵁
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.invoiceId)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    // 鐘舵�佸瓧鍏歌浆涔�
+    statusFormat(status) {
+      const map = { 0: "寰呭鏍�", 1: "宸查�氳繃", 2: "宸查┏鍥�" };
+      return map[status] || "鏈煡";
+    },
+    /** 鎼滅储鎸夐挳鎿嶄綔 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 閲嶇疆鎸夐挳鎿嶄綔 */
+    resetQuery() {
+      this.dateRange = [];
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    /** 璇︽儏鎸夐挳鎿嶄綔 */
+    handleView(row) {
+      this.$router.push({
+        path: '/system/invoice/detail',
+        query: { invoiceId: row.invoiceId }
+      });
+    },
+    /** 瀹℃牳鎸夐挳鎿嶄綔 */
+    handleUpdate(row) {
+      this.$router.push({
+        path: '/system/invoice/audit',
+        query: { invoiceId: row.invoiceId }
+      });
+    },
+    /** 瀵煎嚭鎸夐挳鎿嶄綔 */
+    handleExport() {
+      this.download('system/invoice/export', {
+        ...this.queryParams
+      }, `invoice_${new Date().getTime()}.xlsx`)
+    },
+    /** 鍚屾鎸夐挳鎿嶄綔 */
+    handleSync() {
+      this.loading = true;
+      syncInvoiceStatus().then(() => {
+        this.$modal.msgSuccess("鍚屾鎴愬姛");
+        this.getList();
+      }).finally(() => {
+        this.loading = false;
+      });
+    },
+    /** 涓嬭浇鍙戠エ鎿嶄綔 */
+    handleDownload(row) {
+      window.open(row.invoiceUrl);
+    }
+  }
+};
+</script>
diff --git a/ruoyi-ui/src/views/task/general/detail.vue b/ruoyi-ui/src/views/task/general/detail.vue
index bd5a736..e0b0bd8 100644
--- a/ruoyi-ui/src/views/task/general/detail.vue
+++ b/ruoyi-ui/src/views/task/general/detail.vue
@@ -150,12 +150,42 @@
           <span v-if="taskDetail.emergencyInfo.dispatchSyncErrorMsg" style="color: #F56C6C;">{{ taskDetail.emergencyInfo.dispatchSyncErrorMsg }}</span>
           <span v-else style="color: #C0C4CC;">--</span>
         </el-descriptions-item>
+        <el-descriptions-item label="浠诲姟鐘舵�佸悓姝�" :span="2">
+          <el-alert
+            title="鎻愮ず锛氫换鍔$姸鎬佷細鑷姩鍚屾鍒版棫绯荤粺鐨勮皟搴﹀崟涓紝濡傛灉鍥犵綉缁滅瓑鍘熷洜鏈悓姝ワ紝鍙偣鍑讳笅鏂规寜閽墜鍔ㄥ悓姝ャ��"
+            type="info"
+            :closable="false"
+            show-icon
+            style="margin-bottom: 10px;">
+          </el-alert>
+          <el-button
+            v-if="taskDetail.emergencyInfo.legacyDispatchOrdId && taskDetail.emergencyInfo.legacyDispatchOrdId > 0"
+            type="warning"
+            size="small"
+            icon="el-icon-refresh"
+            :loading="syncingTaskStatus"
+            @click="syncTaskStatus"
+          >鍚屾浠诲姟鐘舵�佸埌鏃х郴缁�</el-button>
+          <el-tag v-else type="info" size="small">
+            <i class="el-icon-warning"></i> 璇峰厛鍚屾璋冨害鍗�
+          </el-tag>
+        </el-descriptions-item>
       </el-descriptions>
 
       <!-- 鏀粯淇℃伅锛堜粎鎬ユ晳杞繍浠诲姟鏄剧ず锛� -->
       <el-card v-if="taskDetail.taskType === 'EMERGENCY_TRANSFER' && paymentInfo" class="box-card" style="margin-top: 20px;">
         <div slot="header" class="clearfix">
           <span>鏀粯淇℃伅</span>
+          <!-- 宸插畬鎴愪笖鏈敵璇峰彂绁ㄦ椂鏄剧ず鐢宠鍙戠エ鎸夐挳 -->
+          <el-button 
+            v-if="canApplyInvoice" 
+            style="float: right; padding: 3px 0" 
+            type="text" 
+            @click="handleApplyInvoice"
+            v-hasPermi="['system:invoice:add']"
+          >
+            <i class="el-icon-document-add"></i> 鐢宠鍙戠エ
+          </el-button>
         </div>
         
         <!-- 鏀粯姒傝 -->
@@ -758,7 +788,7 @@
 </template>
 
 <script>
-import { getTask, updateTask, assignTask, changeTaskStatus, uploadAttachment, deleteAttachment, getTaskVehicles, getAvailableVehicles, assignVehiclesToTask, unassignVehicleFromTask, getPaymentInfo, syncServiceOrder, syncDispatchOrder } from "@/api/task";
+import { getTask, updateTask, assignTask, changeTaskStatus, uploadAttachment, deleteAttachment, getTaskVehicles, getAvailableVehicles, assignVehiclesToTask, unassignVehicleFromTask, getPaymentInfo, syncServiceOrder, syncDispatchOrder, syncTaskStatus } from "@/api/task";
 import { listUser } from "@/api/system/user";
 import { getToken } from "@/utils/auth";
 
@@ -850,7 +880,11 @@
       },
       // 鍚屾鍔犺浇鐘舵��
       syncingServiceOrder: false,
-      syncingDispatchOrder: false
+      syncingDispatchOrder: false,
+      syncingTaskStatus: false,
+      // 鍙戠エ鐢宠鐘舵��
+      hasInvoiceApplied: false,
+      invoiceStatus: null // 0-寰呭鏍�, 1-宸查�氳繃, 2-宸查┏鍥�
     };
   },
   created() {
@@ -859,6 +893,19 @@
     this.getAdditionalFeeList();
     // 鍒濆鍖栦笂浼燯RL
     this.uploadUrl = process.env.VUE_APP_BASE_API + "/task/attachment/upload/" + this.$route.params.taskId;
+    // 妫�鏌ュ彂绁ㄧ敵璇风姸鎬�
+    this.checkInvoiceStatus();
+  },
+  computed: {
+    /** 鏄惁鍙互鐢宠鍙戠エ */
+    canApplyInvoice() {
+      // 鍙湁鎬ユ晳杞繍浠诲姟
+      if (this.taskDetail.taskType !== 'EMERGENCY_TRANSFER') return false;
+      // 浠诲姟蹇呴』宸插畬鎴�
+      if (this.taskDetail.taskStatus !== 'COMPLETED') return false;
+      // 鏈敵璇疯繃鍙戠エ锛屾垨鑰呮浘琚┏鍥�
+      return !this.hasInvoiceApplied || this.invoiceStatus === 2;
+    }
   },
   methods: {
     /** 鑾峰彇浠诲姟璇︽儏 */
@@ -1157,7 +1204,7 @@
       }).then(() => {
         this.$modal.msgSuccess("鏈嶅姟鍗曞悓姝ユ垚鍔�");
         // 閲嶆柊鍔犺浇浠诲姟璇︽儏
-        this.getDetail();
+        this.getTaskDetail();
       }).catch(() => {
         // 澶勭悊鍙栨秷鍜岄敊璇�
       }).finally(() => {
@@ -1172,12 +1219,66 @@
       }).then(() => {
         this.$modal.msgSuccess("璋冨害鍗曞悓姝ユ垚鍔�");
         // 閲嶆柊鍔犺浇浠诲姟璇︽儏
-        this.getDetail();
+        this.getTaskDetail();
       }).catch(() => {
         // 澶勭悊鍙栨秷鍜岄敊璇�
       }).finally(() => {
         this.syncingDispatchOrder = false;
       });
+    },
+    /** 鎵嬪姩鍚屾浠诲姟鐘舵�� */
+    syncTaskStatus() {
+      this.$modal.confirm('鏄惁纭鍚屾浠诲姟鐘舵�佸埌鏃х郴缁燂紵').then(() => {
+        this.syncingTaskStatus = true;
+        return syncTaskStatus(this.taskDetail.taskId);
+      }).then(() => {
+        this.$modal.msgSuccess("浠诲姟鐘舵�佸悓姝ユ垚鍔�");
+        // 閲嶆柊鍔犺浇浠诲姟璇︽儏
+        this.getTaskDetail();
+      }).catch(() => {
+        // 澶勭悊鍙栨秷鍜岄敊璇�
+      }).finally(() => {
+        this.syncingTaskStatus = false;
+      });
+    },
+    
+    /** 妫�鏌ュ彂绁ㄧ敵璇风姸鎬� */
+    checkInvoiceStatus() {
+      // 璋冪敤鍚庣鎺ュ彛妫�鏌ヨ浠诲姟鏄惁宸茬敵璇峰彂绁�
+      this.$axios.get(`/system/invoice/checkTaskInvoice/${this.$route.params.taskId}`)
+        .then(response => {
+          if (response.code === 200 && response.data) {
+            this.hasInvoiceApplied = true;
+            this.invoiceStatus = response.data.status;
+          }
+        })
+        .catch(() => {
+          // 蹇界暐閿欒锛岄粯璁ゆ湭鐢宠
+        });
+    },
+    
+    /** 鐢宠鍙戠エ */
+    handleApplyInvoice() {
+      // 璺宠浆鍒板彂绁ㄧ敵璇烽〉闈紝甯︿笂浠诲姟淇℃伅
+      const taskInfo = {
+        taskId: this.taskDetail.taskId,
+        taskCode: this.taskDetail.taskCode || this.taskDetail.showTaskCode,
+        legacyServiceOrderId: this.taskDetail.emergencyInfo?.legacyServiceOrdId,
+        serviceCode: this.taskDetail.emergencyInfo?.serviceCode,
+        departure: this.taskDetail.departureAddress,
+        destination: this.taskDetail.destinationAddress,
+        completionTime: this.parseTime(this.taskDetail.actualEndTime),
+        transferPrice: this.paymentInfo?.transferPrice || this.paymentInfo?.totalAmount
+      };
+      
+      // 灏嗕换鍔′俊鎭瓨鍌ㄥ埌 sessionStorage
+      sessionStorage.setItem('invoiceTaskInfo', JSON.stringify(taskInfo));
+      
+      // 璺宠浆鍒板彂绁ㄧ敵璇烽〉闈�
+      this.$router.push({
+        path: '/system/invoice/apply',
+        query: { taskId: this.taskDetail.taskId }
+      });
     }
   }
 };
diff --git a/ruoyi-ui/src/views/task/general/index.vue b/ruoyi-ui/src/views/task/general/index.vue
index 382cc4a..3c41b95 100644
--- a/ruoyi-ui/src/views/task/general/index.vue
+++ b/ruoyi-ui/src/views/task/general/index.vue
@@ -29,6 +29,22 @@
           />
         </el-select>
       </el-form-item>
+      <el-form-item label="鍒嗗叕鍙�" prop="deptId">
+        <el-select
+          v-model="queryParams.deptId"
+          placeholder="璇烽�夋嫨鍒嗗叕鍙�"
+          clearable
+          filterable
+          style="width: 200px"
+        >
+          <el-option
+            v-for="dept in branchList"
+            :key="dept.deptId"
+            :label="dept.deptName"
+            :value="dept.deptId"
+          />
+        </el-select>
+      </el-form-item>
       <el-form-item label="杞︾墝鍙�" prop="vehicleNo">
         <el-input
           v-model="queryParams.vehicleNo"
@@ -386,6 +402,7 @@
 <script>
 import { listTask, getTask, delTask, addTask, updateTask, assignTask, changeTaskStatus } from "@/api/task";
 import { listUser } from "@/api/system/user";
+import { listBranchByOa } from "@/api/system/dept";
 
 export default {
   name: "Task",
@@ -425,6 +442,7 @@
         taskCode: null,
         taskType: null,
         taskStatus: null,
+        deptId: null,
         vehicleNo: null,
         plannedStartTimeBegin: null,
         plannedStartTimeEnd: null,
@@ -437,6 +455,8 @@
       statusForm: {},
       // 鐢ㄦ埛鍒楄〃
       userList: [],
+      // 鍒嗗叕鍙稿垪琛�
+      branchList: [],
       // 琛ㄥ崟鏍¢獙
       rules: {
         taskType: [
@@ -469,6 +489,7 @@
   created() {
     this.getList();
     this.getUserList();
+    this.getBranchList();
   },
   methods: {
     /** 鏌ヨ浠诲姟绠$悊鍒楄〃 */
@@ -492,6 +513,14 @@
         this.userList = response.rows;
       });
     },
+    /** 鏌ヨ鍒嗗叕鍙稿垪琛� */
+    getBranchList() {
+      listBranchByOa().then(response => {
+        this.branchList = response.data || [];
+      }).catch(() => {
+        this.branchList = [];
+      });
+    },
     // 鍙栨秷鎸夐挳
     cancel() {
       this.open = false;
diff --git a/sql/InvoiceData.sql b/sql/InvoiceData.sql
new file mode 100644
index 0000000..064800a
--- /dev/null
+++ b/sql/InvoiceData.sql
@@ -0,0 +1,31 @@
+create table InvoiceData(
+InvoiceID	int	no	4	10   	0    	no	(n/a)	(n/a)	NULL
+ServiceOrderIDPK	bigint	no	8	19   	0    	yes	(n/a)	(n/a)	NULL
+InvoiceType	int	no	4	10   	0    	yes	(n/a)	(n/a)	NULL
+InvoiceName	nvarchar	no	100	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+InvoiceMakeout	nvarchar	no	2000	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+InvoiceCompanyPhone	nvarchar	no	100	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+InvoiceCompanyID	nvarchar	no	100	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+InvoiceCompanyAdd	nvarchar	no	200	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+InvoiceCompanyBank	nvarchar	no	100	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+InvoiceCompanyBankNo	nvarchar	no	100	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+InvoiceZipCode	nvarchar	no	100	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+Invoice_strAdd	nvarchar	no	200	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+Invoice_strName	nvarchar	no	100	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+Invoice_strPhone	nvarchar	no	100	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+Invoice_strEmail	nvarchar	no	100	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+ApplicationTime	datetime	no	8	     	     	yes	(n/a)	(n/a)	NULL
+AuditTime	datetime	no	8	     	     	yes	(n/a)	(n/a)	NULL
+AuditStatus	int	no	4	10   	0    	yes	(n/a)	(n/a)	NULL
+AuditOAID	int	no	4	10   	0    	yes	(n/a)	(n/a)	NULL
+AuditMakeout	nvarchar	no	200	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+InvoiceMoney	money	no	8	19   	4    	yes	(n/a)	(n/a)	NULL
+InvoiceNo	nvarchar	no	100	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+InvoiceURL	nvarchar	no	2000	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+InvoiceOddNo	nvarchar	no	400	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+EleCloud_ZTDM	nvarchar	no	100	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+EleCloud_ZTXX	nvarchar	no	200	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+EleCloud_PDF	nvarchar	no	200	     	     	yes	(n/a)	(n/a)	Chinese_PRC_CI_AS
+EleCloud_Time	datetime	no	8	     	     	yes	(n/a)	(n/a)	NULL
+ApplyOAID	int	no	4	10   	0    	yes	(n/a)	(n/a)	NULL
+)
\ No newline at end of file
diff --git a/sql/invoice_menu.sql b/sql/invoice_menu.sql
new file mode 100644
index 0000000..33fa4f9
--- /dev/null
+++ b/sql/invoice_menu.sql
@@ -0,0 +1,27 @@
+-- ----------------------------
+-- 1銆佸彂绁ㄧ鐞嗕富鑿滃崟
+-- ----------------------------
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark)
+values('鍙戠エ绠$悊', '1', '10', 'invoice', 'system/invoice/index', 1, 0, 'C', '0', '0', 'system:invoice:list', 'edit', 'admin', sysdate(), '鍙戠エ鐢宠绠$悊鑿滃崟');
+
+-- 鑾峰彇鍒氬垰鎻掑叆鐨勮彍鍗旾D (閫傜敤浜嶮ySQL)
+set @parentId = LAST_INSERT_ID();
+
+-- ----------------------------
+-- 2銆佸彂绁ㄧ鐞嗙浉鍏虫寜閽潈闄�
+-- ----------------------------
+-- 鏌ヨ鏉冮檺
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark)
+values('鍙戠エ鏌ヨ', @parentId, '1',  '', '', 1, 0, 'F', '0', '0', 'system:invoice:query',  '#', 'admin', sysdate(), '');
+
+-- 淇敼/瀹℃牳鏉冮檺
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark)
+values('鍙戠エ瀹℃牳', @parentId, '2',  '', '', 1, 0, 'F', '0', '0', 'system:invoice:edit',   '#', 'admin', sysdate(), '');
+
+-- 鍒犻櫎鏉冮檺
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark)
+values('鍙戠エ鍒犻櫎', @parentId, '3',  '', '', 1, 0, 'F', '0', '0', 'system:invoice:remove', '#', 'admin', sysdate(), '');
+
+-- 瀵煎嚭鏉冮檺
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark)
+values('鍙戠エ瀵煎嚭', @parentId, '4',  '', '', 1, 0, 'F', '0', '0', 'system:invoice:export', '#', 'admin', sysdate(), '');
\ No newline at end of file
diff --git a/sql/invoice_sync_from_legacy.sql b/sql/invoice_sync_from_legacy.sql
new file mode 100644
index 0000000..ccb207d
--- /dev/null
+++ b/sql/invoice_sync_from_legacy.sql
@@ -0,0 +1,124 @@
+-- 浠庢棫绯荤粺鍚屾鍙戠エ淇℃伅鍒版柊绯荤粺鐨凷QL鑴氭湰
+-- 鍚屾鏃х郴缁熺殑鍙戠エ鏁版嵁鍒版柊绯荤粺锛岄伩鍏嶉噸澶嶅悓姝�
+
+-- 鏂规1: 浣跨敤INSERT IGNORE鎻掑叆鏂版暟鎹�
+INSERT IGNORE INTO sys_invoice (
+    legacy_invoice_id,
+    legacy_service_order_id,
+    invoice_type,
+    invoice_name,
+    invoice_money,
+    invoice_remarks,
+    company_address,
+    company_bank,
+    company_bank_no,
+    zip_code,
+    mail_address,
+    contact_name,
+    contact_phone,
+    contact_email,
+    status,
+    invoice_no,
+    invoice_url,
+    apply_time,
+    audit_time,
+    audit_remarks,
+    sync_status
+)
+SELECT 
+    i.InvoiceID as legacy_invoice_id,
+    i.ServiceOrderIDPK as legacy_service_order_id,
+    CASE 
+        WHEN i.InvoiceType = 1 THEN 1  -- 涓汉鍙戠エ
+        WHEN i.InvoiceType = 2 THEN 2  -- 浼佷笟鍙戠エ
+        ELSE 1  -- 榛樿涓汉鍙戠エ
+    END as invoice_type,
+    i.InvoiceName as invoice_name,
+    CAST(i.InvoiceMoney AS DECIMAL(10,2)) as invoice_money,
+    i.InvoiceMakeout as invoice_remarks,
+    i.InvoiceCompanyAdd as company_address,
+    i.InvoiceCompanyBank as company_bank,
+    i.InvoiceCompanyBankNo as company_bank_no,
+    i.InvoiceZipCode as zip_code,
+    i.Invoice_strAdd as mail_address,
+    i.Invoice_strName as contact_name,
+    i.Invoice_strPhone as contact_phone,
+    i.Invoice_strEmail as contact_email,
+    CASE 
+        WHEN i.AuditStatus = 1 THEN 1  -- 宸查�氳繃
+        WHEN i.AuditStatus = 2 THEN 2  -- 宸查┏鍥�
+        ELSE 0  -- 寰呭鏍�
+    END as status,
+    i.InvoiceNo as invoice_no,
+    COALESCE(i.InvoiceURL, i.EleCloud_PDF) as invoice_url,
+    i.ApplicationTime as apply_time,
+    i.AuditTime as audit_time,
+    i.AuditMakeout as audit_remarks,
+    1 as sync_status  -- 鏍囪涓哄凡鍚屾
+FROM InvoiceData i
+WHERE i.InvoiceID NOT IN (
+    SELECT legacy_invoice_id 
+    FROM sys_invoice 
+    WHERE legacy_invoice_id IS NOT NULL
+);
+
+-- 鏂规2: 浣跨敤LEFT JOIN纭繚鍙彃鍏ヤ笉瀛樺湪鐨勮褰�
+-- INSERT INTO sys_invoice (
+--     legacy_invoice_id,
+--     legacy_service_order_id,
+--     invoice_type,
+--     invoice_name,
+--     invoice_money,
+--     invoice_remarks,
+--     company_address,
+--     company_bank,
+--     company_bank_no,
+--     zip_code,
+--     mail_address,
+--     contact_name,
+--     contact_phone,
+--     contact_email,
+--     status,
+--     invoice_no,
+--     invoice_url,
+--     apply_time,
+--     audit_time,
+--     audit_remarks,
+--     sync_status
+-- )
+-- SELECT 
+--     i.InvoiceID,
+--     i.ServiceOrderIDPK,
+--     CASE 
+--         WHEN i.InvoiceType = 1 THEN 1
+--         WHEN i.InvoiceType = 2 THEN 2
+--         ELSE 1
+--     END,
+--     i.InvoiceName,
+--     CAST(i.InvoiceMoney AS DECIMAL(10,2)),
+--     i.InvoiceMakeout,
+--     i.InvoiceCompanyAdd,
+--     i.InvoiceCompanyBank,
+--     i.InvoiceCompanyBankNo,
+--     i.InvoiceZipCode,
+--     i.Invoice_strAdd,
+--     i.Invoice_strName,
+--     i.Invoice_strPhone,
+--     i.Invoice_strEmail,
+--     CASE 
+--         WHEN i.AuditStatus = 1 THEN 1
+--         WHEN i.AuditStatus = 2 THEN 2
+--         ELSE 0
+--     END,
+--     i.InvoiceNo,
+--     COALESCE(i.InvoiceURL, i.EleCloud_PDF),
+--     i.ApplicationTime,
+--     i.AuditTime,
+--     i.AuditMakeout,
+--     1
+-- FROM InvoiceData i
+-- LEFT JOIN sys_invoice si ON i.InvoiceID = si.legacy_invoice_id
+-- WHERE si.legacy_invoice_id IS NULL;
+
+-- 鏇存柊缁熻淇℃伅
+ANALYZE TABLE sys_invoice;
\ No newline at end of file
diff --git a/sql/invoice_sync_job.sql b/sql/invoice_sync_job.sql
new file mode 100644
index 0000000..792ae94
--- /dev/null
+++ b/sql/invoice_sync_job.sql
@@ -0,0 +1,248 @@
+-- 鍙戠エ鍚屾瀹氭椂浠诲姟閰嶇疆
+-- 鐢ㄤ簬浠庢棫绯荤粺鍚屾鍙戠エ淇℃伅鍒版柊绯荤粺
+
+-- 1. 鍒涘缓鍙戠エ鍚屾鏃ュ織琛�
+CREATE TABLE IF NOT EXISTS `sys_invoice_sync_log` (
+  `log_id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '鍚屾鏃ュ織ID',
+  `sync_type` VARCHAR(50) NOT NULL COMMENT '鍚屾绫诲瀷(invoice_info-鍙戠エ淇℃伅,invoice_status-鍙戠エ鐘舵��)',
+  `sync_start_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '鍚屾寮�濮嬫椂闂�',
+  `sync_end_time` DATETIME DEFAULT NULL COMMENT '鍚屾缁撴潫鏃堕棿',
+  `records_processed` INT(11) DEFAULT 0 COMMENT '澶勭悊璁板綍鏁�',
+  `records_success` INT(11) DEFAULT 0 COMMENT '鎴愬姛璁板綍鏁�',
+  `records_failed` INT(11) DEFAULT 0 COMMENT '澶辫触璁板綍鏁�',
+  `error_message` TEXT DEFAULT NULL COMMENT '閿欒淇℃伅',
+  `status` TINYINT(1) DEFAULT 0 COMMENT '鐘舵��(0-澶勭悊涓�,1-鎴愬姛,2-澶辫触)',
+  PRIMARY KEY (`log_id`),
+  INDEX `idx_sync_type` (`sync_type`),
+  INDEX `idx_sync_start_time` (`sync_start_time`)
+) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='鍙戠エ鍚屾鏃ュ織琛�';
+
+-- 2. 鎻掑叆鍙戠エ鍚屾瀹氭椂浠诲姟閰嶇疆
+DELETE FROM sys_job WHERE job_name = 'InvoiceSyncJob';
+INSERT INTO sys_job (
+    job_name, 
+    job_group, 
+    invoke_target, 
+    cron_expression, 
+    misfire_policy, 
+    concurrent, 
+    status, 
+    create_by, 
+    create_time
+) VALUES (
+    'InvoiceSyncJob',
+    'SYSTEM',
+    'sysInvoiceTask.syncInvoiceFromLegacySystem',
+    '0 0/30 * * * ?',  -- 姣�30鍒嗛挓鎵ц涓�娆�
+    '3',
+    '1',
+    '0',
+    'admin',
+    NOW()
+);
+
+-- 3. 鍚屾鍙戠エ淇℃伅鐨勫瓨鍌ㄨ繃绋�
+DELIMITER $$
+
+DROP PROCEDURE IF EXISTS sync_invoice_from_legacy$$
+
+CREATE PROCEDURE sync_invoice_from_legacy()
+BEGIN
+    DECLARE v_start_time DATETIME DEFAULT NOW();
+    DECLARE v_error_msg TEXT DEFAULT '';
+    DECLARE v_processed_count INT DEFAULT 0;
+    DECLARE v_success_count INT DEFAULT 0;
+    DECLARE v_failed_count INT DEFAULT 0;
+    DECLARE EXIT HANDLER FOR SQLEXCEPTION
+    BEGIN
+        GET DIAGNOSTICS CONDITION 1
+            v_error_msg = MESSAGE_TEXT;
+        ROLLBACK;
+    END;
+
+    START TRANSACTION;
+    
+    -- 璁板綍寮�濮嬪悓姝�
+    INSERT INTO sys_invoice_sync_log (sync_type, sync_start_time, status)
+    VALUES ('invoice_info', v_start_time, 0);
+    
+    SET @log_id = LAST_INSERT_ID();
+    
+    -- 鍚屾鏂扮殑鍙戠エ璁板綍
+    INSERT INTO sys_invoice (
+        legacy_invoice_id,
+        legacy_service_order_id,
+        invoice_type,
+        invoice_name,
+        invoice_money,
+        invoice_remarks,
+        company_address,
+        company_bank,
+        company_bank_no,
+        zip_code,
+        mail_address,
+        contact_name,
+        contact_phone,
+        contact_email,
+        status,
+        invoice_no,
+        invoice_url,
+        apply_time,
+        audit_time,
+        audit_remarks,
+        sync_status
+    )
+    SELECT 
+        i.InvoiceID as legacy_invoice_id,
+        i.ServiceOrderIDPK as legacy_service_order_id,
+        CASE 
+            WHEN i.InvoiceType = 1 THEN 1  -- 涓汉鍙戠エ
+            WHEN i.InvoiceType = 2 THEN 2  -- 浼佷笟鍙戠エ
+            ELSE 1  -- 榛樿涓汉鍙戠エ
+        END as invoice_type,
+        i.InvoiceName as invoice_name,
+        CAST(i.InvoiceMoney AS DECIMAL(10,2)) as invoice_money,
+        i.InvoiceMakeout as invoice_remarks,
+        i.InvoiceCompanyAdd as company_address,
+        i.InvoiceCompanyBank as company_bank,
+        i.InvoiceCompanyBankNo as company_bank_no,
+        i.InvoiceZipCode as zip_code,
+        i.Invoice_strAdd as mail_address,
+        i.Invoice_strName as contact_name,
+        i.Invoice_strPhone as contact_phone,
+        i.Invoice_strEmail as contact_email,
+        CASE 
+            WHEN i.AuditStatus = 1 THEN 1  -- 宸查�氳繃
+            WHEN i.AuditStatus = 2 THEN 2  -- 宸查┏鍥�
+            ELSE 0  -- 寰呭鏍�
+        END as status,
+        i.InvoiceNo as invoice_no,
+        COALESCE(i.InvoiceURL, i.EleCloud_PDF) as invoice_url,
+        i.ApplicationTime as apply_time,
+        i.AuditTime as audit_time,
+        i.AuditMakeout as audit_remarks,
+        1 as sync_status  -- 鏍囪涓哄凡鍚屾
+    FROM InvoiceData i
+    WHERE i.InvoiceID NOT IN (
+        SELECT legacy_invoice_id 
+        FROM sys_invoice 
+        WHERE legacy_invoice_id IS NOT NULL
+    );
+    
+    SET v_processed_count = ROW_COUNT();
+    SET v_success_count = v_processed_count;
+    
+    -- 鏇存柊鍚屾鏃ュ織
+    UPDATE sys_invoice_sync_log 
+    SET 
+        sync_end_time = NOW(),
+        records_processed = v_processed_count,
+        records_success = v_success_count,
+        records_failed = v_failed_count,
+        error_message = v_error_msg,
+        status = IF(v_error_msg = '', 1, 2)
+    WHERE log_id = @log_id;
+    
+    COMMIT;
+END$$
+
+DELIMITER ;
+
+-- 4. 鍚屾鍙戠エ鐘舵�佺殑瀛樺偍杩囩▼
+DELIMITER $$
+
+DROP PROCEDURE IF EXISTS sync_invoice_status_from_legacy$$
+
+CREATE PROCEDURE sync_invoice_status_from_legacy()
+BEGIN
+    DECLARE v_start_time DATETIME DEFAULT NOW();
+    DECLARE v_error_msg TEXT DEFAULT '';
+    DECLARE v_processed_count INT DEFAULT 0;
+    DECLARE v_success_count INT DEFAULT 0;
+    DECLARE v_failed_count INT DEFAULT 0;
+    DECLARE EXIT HANDLER FOR SQLEXCEPTION
+    BEGIN
+        GET DIAGNOSTICS CONDITION 1
+            v_error_msg = MESSAGE_TEXT;
+        ROLLBACK;
+    END;
+
+    START TRANSACTION;
+    
+    -- 璁板綍寮�濮嬪悓姝�
+    INSERT INTO sys_invoice_sync_log (sync_type, sync_start_time, status)
+    VALUES ('invoice_status', v_start_time, 0);
+    
+    SET @log_id = LAST_INSERT_ID();
+    
+    -- 鏇存柊鐜版湁鍙戠エ鐨勭姸鎬佷俊鎭�
+    UPDATE sys_invoice si
+    INNER JOIN InvoiceData i ON si.legacy_invoice_id = i.InvoiceID
+    SET 
+        si.status = CASE 
+            WHEN i.AuditStatus = 1 THEN 1  -- 宸查�氳繃
+            WHEN i.AuditStatus = 2 THEN 2  -- 宸查┏鍥�
+            ELSE 0  -- 寰呭鏍�
+        END,
+        si.invoice_no = COALESCE(si.invoice_no, i.InvoiceNo),
+        si.invoice_url = COALESCE(si.invoice_url, i.InvoiceURL, i.EleCloud_PDF),
+        si.audit_time = i.AuditTime,
+        si.audit_remarks = i.AuditMakeout,
+        si.sync_status = 1
+    WHERE si.legacy_invoice_id IS NOT NULL
+      AND (
+          si.status != CASE 
+              WHEN i.AuditStatus = 1 THEN 1
+              WHEN i.AuditStatus = 2 THEN 2
+              ELSE 0
+          END
+          OR si.invoice_no IS NULL
+          OR si.invoice_url IS NULL
+      );
+    
+    SET v_processed_count = ROW_COUNT();
+    SET v_success_count = v_processed_count;
+    
+    -- 鏇存柊鍚屾鏃ュ織
+    UPDATE sys_invoice_sync_log 
+    SET 
+        sync_end_time = NOW(),
+        records_processed = v_processed_count,
+        records_success = v_success_count,
+        records_failed = v_failed_count,
+        error_message = v_error_msg,
+        status = IF(v_error_msg = '', 1, 2)
+    WHERE log_id = @log_id;
+    
+    COMMIT;
+END$$
+
+DELIMITER ;
+
+-- 5. 鏌ヨ鍙戠エ鍚屾鏃ュ織鐨勮鍥�
+CREATE OR REPLACE VIEW v_invoice_sync_log AS
+SELECT 
+    l.log_id,
+    l.sync_type,
+    l.sync_start_time,
+    l.sync_end_time,
+    l.records_processed,
+    l.records_success,
+    l.records_failed,
+    l.error_message,
+    l.status,
+    CASE l.status 
+        WHEN 0 THEN '澶勭悊涓�' 
+        WHEN 1 THEN '鎴愬姛' 
+        WHEN 2 THEN '澶辫触' 
+        ELSE '鏈煡' 
+    END as status_name,
+    TIMESTAMPDIFF(SECOND, l.sync_start_time, l.sync_end_time) as duration_seconds
+FROM sys_invoice_sync_log l
+ORDER BY l.sync_start_time DESC;
+
+-- 6. 鎵嬪姩鎵ц鍙戠エ淇℃伅鍚屾
+-- CALL sync_invoice_from_legacy();
+
+-- 7. 鎵嬪姩鎵ц鍙戠エ鐘舵�佸悓姝�
+-- CALL sync_invoice_status_from_legacy();
\ No newline at end of file
diff --git a/sql/invoice_sys.sql b/sql/invoice_sys.sql
new file mode 100644
index 0000000..97fe8bb
--- /dev/null
+++ b/sql/invoice_sys.sql
@@ -0,0 +1,32 @@
+-- 鍙戠エ鐢宠琛�
+CREATE TABLE `sys_invoice` (
+  `invoice_id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '鍙戠エID',
+  `service_order_id` BIGINT(20) DEFAULT NULL COMMENT '鏈嶅姟鍗曞彿(鏂扮郴缁熻鍗旾D)',
+  `legacy_service_order_id` BIGINT(20) DEFAULT NULL COMMENT '鏈嶅姟鍗曞彿(鏃х郴缁烻erviceOrderID)',
+  `invoice_type` INT(1) DEFAULT '1' COMMENT '寮�绁ㄧ被鍨�(1-涓汉, 2-浼佷笟)',
+  `invoice_name` VARCHAR(200) DEFAULT NULL COMMENT '鍙戠エ鎶ご',
+  `invoice_money` DECIMAL(10,2) DEFAULT '0.00' COMMENT '鍙戠エ閲戦',
+  `invoice_remarks` VARCHAR(500) DEFAULT NULL COMMENT '鍙戠エ澶囨敞',
+  `company_address` VARCHAR(500) DEFAULT NULL COMMENT '浼佷笟娉ㄥ唽鍦板潃',
+  `company_bank` VARCHAR(200) DEFAULT NULL COMMENT '浼佷笟寮�鎴烽摱琛�',
+  `company_bank_no` VARCHAR(100) DEFAULT NULL COMMENT '浼佷笟閾惰甯愬彿',
+  `zip_code` VARCHAR(20) DEFAULT NULL COMMENT '閭紪',
+  `mail_address` VARCHAR(500) DEFAULT NULL COMMENT '閭瘎鍦板潃',
+  `contact_name` VARCHAR(50) DEFAULT NULL COMMENT '鑱旂郴浜�',
+  `contact_phone` VARCHAR(50) DEFAULT NULL COMMENT '鑱旂郴鐢佃瘽',
+  `contact_email` VARCHAR(100) DEFAULT NULL COMMENT '鑱旂郴閭',
+  `status` INT(1) DEFAULT '0' COMMENT '鐢宠鐘舵��(0-寰呭鏍�, 1-宸查�氳繃, 2-宸查┏鍥�)',
+  `invoice_no` VARCHAR(100) DEFAULT NULL COMMENT '鍙戠エ缂栧彿(瀵瑰簲鏃х郴缁烮nvoiceNo)',
+  `invoice_url` VARCHAR(2000) DEFAULT NULL COMMENT '鍙戠エ閾炬帴/鏂囦欢鍦板潃(瀵瑰簲鏃х郴缁烮nvoiceURL/EleCloud_PDF)',
+  `apply_user_id` BIGINT(20) DEFAULT NULL COMMENT '鐢宠浜篒D',
+  `apply_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '鐢宠鏃堕棿',
+  `audit_user_id` BIGINT(20) DEFAULT NULL COMMENT '瀹℃牳浜篒D',
+  `audit_time` DATETIME DEFAULT NULL COMMENT '瀹℃牳鏃堕棿',
+  `audit_remarks` VARCHAR(500) DEFAULT NULL COMMENT '瀹℃牳澶囨敞',
+  `sync_status` INT(1) DEFAULT '0' COMMENT '鍚屾鐘舵��(0-鏈悓姝�, 1-宸插悓姝�, 2-鍚屾澶辫触)',
+  `legacy_invoice_id` INT(11) DEFAULT NULL COMMENT '鏃х郴缁熷彂绁↖D(瀵瑰簲鏃х郴缁烮nvoiceID)',
+  PRIMARY KEY (`invoice_id`),
+  KEY `idx_service_order` (`service_order_id`),
+  KEY `idx_legacy_order` (`legacy_service_order_id`),
+  KEY `idx_apply_user` (`apply_user_id`)
+) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='鍙戠エ鐢宠琛�';
\ No newline at end of file
diff --git a/sql/partition_quick_setup.sql b/sql/partition_quick_setup.sql
new file mode 100644
index 0000000..0abd88f
--- /dev/null
+++ b/sql/partition_quick_setup.sql
@@ -0,0 +1,260 @@
+-- ========================================
+-- GPS鍒嗘閲岀▼琛ㄥ垎鍖轰紭鍖� - 蹇�熸墽琛岀増
+-- ========================================
+-- 閫傜敤鍦烘櫙锛氭暟鎹噺閫備腑锛�100涓�-500涓囷級锛屽彲浠ユ帴鍙楃煭鏆傚仠鏈�
+-- 鎵ц鏃堕棿锛氭牴鎹暟鎹噺锛岄璁�5-30鍒嗛挓
+-- ========================================
+
+-- 绗竴姝ワ細妫�鏌ュ綋鍓嶆暟鎹姸鎬�
+-- ========================================
+USE your_database_name; -- 璇蜂慨鏀逛负瀹為檯鐨勬暟鎹簱鍚�
+
+SELECT '=== 褰撳墠琛ㄤ俊鎭� ===' as info;
+SELECT 
+    TABLE_NAME as '琛ㄥ悕',
+    TABLE_ROWS as '璁板綍鏁�(浼扮畻)',
+    ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS '澶у皬(MB)'
+FROM information_schema.TABLES
+WHERE TABLE_SCHEMA = DATABASE()
+  AND TABLE_NAME = 'tb_vehicle_gps_segment_mileage';
+
+SELECT '=== 鏁版嵁鏃堕棿鑼冨洿 ===' as info;
+SELECT 
+    MIN(segment_start_time) as '鏈�鏃╂暟鎹�',
+    MAX(segment_start_time) as '鏈�鏂版暟鎹�',
+    DATEDIFF(MAX(segment_start_time), MIN(segment_start_time)) as '鏁版嵁璺ㄥ害(澶�)'
+FROM tb_vehicle_gps_segment_mileage;
+
+SELECT '=== 鎸夋湀鏁版嵁鍒嗗竷 ===' as info;
+SELECT 
+    DATE_FORMAT(segment_start_time, '%Y-%m') as '鏈堜唤',
+    COUNT(*) as '璁板綍鏁�',
+    ROUND(SUM(segment_distance), 2) as '鎬婚噷绋�(km)'
+FROM tb_vehicle_gps_segment_mileage
+GROUP BY DATE_FORMAT(segment_start_time, '%Y-%m')
+ORDER BY 1 DESC
+LIMIT 12;
+
+-- 鏆傚仠锛佽妫�鏌ヤ互涓婁俊鎭紝纭鏁版嵁閲忓拰鏃堕棿鑼冨洿
+-- 鎸夊洖杞︾户缁�...
+
+-- ========================================
+-- 绗簩姝ワ細鍒涘缓鍒嗗尯琛�
+-- ========================================
+
+-- 鍒涘缓鏂扮殑鍒嗗尯琛�
+CREATE TABLE `tb_vehicle_gps_segment_mileage_partitioned` (
+  `segment_id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `vehicle_id` bigint(20) NOT NULL,
+  `vehicle_no` varchar(20) DEFAULT NULL,
+  `segment_start_time` datetime NOT NULL,
+  `segment_end_time` datetime NOT NULL,
+  `start_longitude` decimal(10,7) DEFAULT NULL,
+  `start_latitude` decimal(10,7) DEFAULT NULL,
+  `end_longitude` decimal(10,7) DEFAULT NULL,
+  `end_latitude` decimal(10,7) DEFAULT NULL,
+  `segment_distance` decimal(10,3) DEFAULT 0.000,
+  `gps_point_count` int(11) DEFAULT 0,
+  `gps_ids` text,
+  `task_id` bigint(20) DEFAULT NULL,
+  `task_code` varchar(50) DEFAULT NULL,
+  `calculate_method` varchar(20) DEFAULT 'tianditu',
+  `create_time` datetime DEFAULT NULL,
+  `update_time` datetime DEFAULT NULL,
+  PRIMARY KEY (`segment_id`, `segment_start_time`),
+  UNIQUE KEY `uk_vehicle_time` (`vehicle_id`, `segment_start_time`),
+  KEY `idx_vehicle_id` (`vehicle_id`),
+  KEY `idx_start_time` (`segment_start_time`),
+  KEY `idx_task_id` (`task_id`),
+  KEY `idx_vehicle_task` (`vehicle_id`, `task_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 
+COMMENT='杞﹁締GPS鍒嗘閲岀▼琛紙鎸夋湀鍒嗗尯锛�'
+PARTITION BY RANGE (TO_DAYS(segment_start_time)) (
+    PARTITION p202401 VALUES LESS THAN (TO_DAYS('2024-02-01')),
+    PARTITION p202402 VALUES LESS THAN (TO_DAYS('2024-03-01')),
+    PARTITION p202403 VALUES LESS THAN (TO_DAYS('2024-04-01')),
+    PARTITION p202404 VALUES LESS THAN (TO_DAYS('2024-05-01')),
+    PARTITION p202405 VALUES LESS THAN (TO_DAYS('2024-06-01')),
+    PARTITION p202406 VALUES LESS THAN (TO_DAYS('2024-07-01')),
+    PARTITION p202407 VALUES LESS THAN (TO_DAYS('2024-08-01')),
+    PARTITION p202408 VALUES LESS THAN (TO_DAYS('2024-09-01')),
+    PARTITION p202409 VALUES LESS THAN (TO_DAYS('2024-10-01')),
+    PARTITION p202410 VALUES LESS THAN (TO_DAYS('2024-11-01')),
+    PARTITION p202411 VALUES LESS THAN (TO_DAYS('2024-12-01')),
+    PARTITION p202412 VALUES LESS THAN (TO_DAYS('2025-01-01')),
+    PARTITION p202501 VALUES LESS THAN (TO_DAYS('2025-02-01')),
+    PARTITION p202502 VALUES LESS THAN (TO_DAYS('2025-03-01')),
+    PARTITION p202503 VALUES LESS THAN (TO_DAYS('2025-04-01')),
+    PARTITION p202504 VALUES LESS THAN (TO_DAYS('2025-05-01')),
+    PARTITION p202505 VALUES LESS THAN (TO_DAYS('2025-06-01')),
+    PARTITION p202506 VALUES LESS THAN (TO_DAYS('2025-07-01')),
+    PARTITION p202507 VALUES LESS THAN (TO_DAYS('2025-08-01')),
+    PARTITION p202508 VALUES LESS THAN (TO_DAYS('2025-09-01')),
+    PARTITION p202509 VALUES LESS THAN (TO_DAYS('2025-10-01')),
+    PARTITION p202510 VALUES LESS THAN (TO_DAYS('2025-11-01')),
+    PARTITION p202511 VALUES LESS THAN (TO_DAYS('2025-12-01')),
+    PARTITION p202512 VALUES LESS THAN (TO_DAYS('2026-01-01')),
+    PARTITION p202601 VALUES LESS THAN (TO_DAYS('2026-02-01')),
+    PARTITION p202602 VALUES LESS THAN (TO_DAYS('2026-03-01')),
+    PARTITION p202603 VALUES LESS THAN (TO_DAYS('2026-04-01')),
+    PARTITION p202604 VALUES LESS THAN (TO_DAYS('2026-05-01')),
+    PARTITION p202605 VALUES LESS THAN (TO_DAYS('2026-06-01')),
+    PARTITION p202606 VALUES LESS THAN (TO_DAYS('2026-07-01')),
+    PARTITION p202607 VALUES LESS THAN (TO_DAYS('2026-08-01')),
+    PARTITION p202608 VALUES LESS THAN (TO_DAYS('2026-09-01')),
+    PARTITION p202609 VALUES LESS THAN (TO_DAYS('2026-10-01')),
+    PARTITION p202610 VALUES LESS THAN (TO_DAYS('2026-11-01')),
+    PARTITION p202611 VALUES LESS THAN (TO_DAYS('2026-12-01')),
+    PARTITION p202612 VALUES LESS THAN (TO_DAYS('2027-01-01')),
+    PARTITION pfuture VALUES LESS THAN MAXVALUE
+);
+
+SELECT '鍒嗗尯琛ㄥ垱寤烘垚鍔�' as result;
+
+-- ========================================
+-- 绗笁姝ワ細杩佺Щ鏁版嵁
+-- ========================================
+SELECT '寮�濮嬭縼绉绘暟鎹�...' as info, NOW() as start_time;
+
+-- 涓�娆℃�ц縼绉伙紙閫傜敤浜庢暟鎹噺涓嶈秴杩�500涓囷級
+INSERT INTO tb_vehicle_gps_segment_mileage_partitioned 
+SELECT * FROM tb_vehicle_gps_segment_mileage;
+
+SELECT '鏁版嵁杩佺Щ瀹屾垚' as info, NOW() as end_time;
+
+-- ========================================
+-- 绗洓姝ワ細楠岃瘉鏁版嵁
+-- ========================================
+SELECT '=== 鏁版嵁楠岃瘉 ===' as info;
+
+-- 姣旇緝璁板綍鏁�
+SELECT 
+    '鍘熻〃' as table_name, 
+    COUNT(*) as record_count 
+FROM tb_vehicle_gps_segment_mileage
+UNION ALL
+SELECT 
+    '鏂拌〃(鍒嗗尯)' as table_name, 
+    COUNT(*) as record_count 
+FROM tb_vehicle_gps_segment_mileage_partitioned;
+
+-- 姣旇緝缁熻鏁版嵁
+SELECT 
+    '鍘熻〃' as table_name,
+    SUM(segment_distance) as total_distance,
+    MIN(segment_start_time) as min_time,
+    MAX(segment_start_time) as max_time
+FROM tb_vehicle_gps_segment_mileage
+UNION ALL
+SELECT 
+    '鏂拌〃(鍒嗗尯)' as table_name,
+    SUM(segment_distance) as total_distance,
+    MIN(segment_start_time) as min_time,
+    MAX(segment_start_time) as max_time
+FROM tb_vehicle_gps_segment_mileage_partitioned;
+
+-- 鏌ョ湅鍒嗗尯鍒嗗竷
+SELECT 
+    PARTITION_NAME as '鍒嗗尯鍚�',
+    TABLE_ROWS as '璁板綍鏁�',
+    ROUND(DATA_LENGTH/1024/1024, 2) as '鏁版嵁(MB)',
+    ROUND(INDEX_LENGTH/1024/1024, 2) as '绱㈠紩(MB)'
+FROM information_schema.PARTITIONS
+WHERE TABLE_SCHEMA = DATABASE()
+  AND TABLE_NAME = 'tb_vehicle_gps_segment_mileage_partitioned'
+ORDER BY PARTITION_ORDINAL_POSITION;
+
+-- 鏆傚仠锛佽妫�鏌ユ暟鎹槸鍚︿竴鑷�
+-- 濡傛灉鏁版嵁涓�鑷达紝缁х画鎵ц涓嬩竴姝�
+-- 濡傛灉涓嶄竴鑷达紝璇峰仠姝㈠苟妫�鏌ラ棶棰�
+
+-- ========================================
+-- 绗簲姝ワ細鍒囨崲琛ㄥ悕锛堣皑鎱庯紒锛�
+-- ========================================
+-- 寤鸿鍦ㄤ笟鍔″仠鏈虹獥鍙f墽琛屼互涓嬫搷浣�
+
+-- START TRANSACTION;
+
+SELECT '寮�濮嬪垏鎹㈣〃鍚�...' as info;
+
+-- 閲嶅懡鍚嶅師琛ㄤ负澶囦唤琛�
+RENAME TABLE 
+    tb_vehicle_gps_segment_mileage TO tb_vehicle_gps_segment_mileage_old,
+    tb_vehicle_gps_segment_mileage_partitioned TO tb_vehicle_gps_segment_mileage;
+
+SELECT '琛ㄥ悕鍒囨崲瀹屾垚锛岃绔嬪嵆楠岃瘉搴旂敤鍔熻兘锛�' as result;
+
+-- 濡傛灉鏈夐棶棰橈紝绔嬪嵆鍥炴粴锛�
+-- RENAME TABLE 
+--     tb_vehicle_gps_segment_mileage TO tb_vehicle_gps_segment_mileage_partitioned,
+--     tb_vehicle_gps_segment_mileage_old TO tb_vehicle_gps_segment_mileage;
+
+-- COMMIT;
+
+-- ========================================
+-- 绗叚姝ワ細楠岃瘉鍒嗗尯鏁堟灉
+-- ========================================
+SELECT '=== 娴嬭瘯鏌ヨ鎬ц兘 ===' as info;
+
+-- 娴嬭瘯1锛氭煡璇㈡渶杩�1鏈堟暟鎹紙搴旇鍙壂鎻�1涓垎鍖猴級
+EXPLAIN PARTITIONS
+SELECT COUNT(*), SUM(segment_distance) 
+FROM tb_vehicle_gps_segment_mileage
+WHERE segment_start_time >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH);
+
+-- 娴嬭瘯2锛氭寜杞﹁締ID鏌ヨ鏈�杩�1鍛ㄦ暟鎹�
+EXPLAIN PARTITIONS
+SELECT * 
+FROM tb_vehicle_gps_segment_mileage
+WHERE vehicle_id = 1
+  AND segment_start_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
+LIMIT 10;
+
+-- 娴嬭瘯3锛氱粺璁℃渶杩�3涓湀鐨勯噷绋�
+SELECT 
+    DATE_FORMAT(segment_start_time, '%Y-%m') as month,
+    COUNT(*) as segments,
+    ROUND(SUM(segment_distance), 2) as total_km
+FROM tb_vehicle_gps_segment_mileage
+WHERE segment_start_time >= DATE_SUB(CURDATE(), INTERVAL 3 MONTH)
+GROUP BY DATE_FORMAT(segment_start_time, '%Y-%m');
+
+-- ========================================
+-- 绗竷姝ワ細娓呯悊鍜屼紭鍖栵紙鍙�夛級
+-- ========================================
+
+-- 纭搴旂敤杩愯姝e父鍚庯紝绛夊緟1-2鍛紝鐒跺悗鍒犻櫎澶囦唤琛�
+-- DROP TABLE tb_vehicle_gps_segment_mileage_old;
+
+-- 鍒嗘瀽琛紝浼樺寲鏌ヨ璁″垝
+ANALYZE TABLE tb_vehicle_gps_segment_mileage;
+
+-- ========================================
+-- 瀹屾垚锛�
+-- ========================================
+SELECT '========================================' as info;
+SELECT '鍒嗗尯浼樺寲瀹屾垚锛�' as result;
+SELECT '璇锋敞鎰忥細' as notice;
+SELECT '1. 瀹氭湡娣诲姞鏂版湀浠界殑鍒嗗尯' as task1;
+SELECT '2. 瀹氭湡娓呯悊鍘嗗彶鍒嗗尯閲婃斁绌洪棿' as task2;
+SELECT '3. 鏌ヨ鏃跺敖閲忓甫涓婃椂闂磋寖鍥存潯浠�' as task3;
+SELECT '========================================' as info;
+
+-- ========================================
+-- 鏃ュ父缁存姢鍛戒护鍙傝��
+-- ========================================
+
+-- 娣诲姞鏂板垎鍖猴紙姣忔湀鎵ц涓�娆★級
+-- ALTER TABLE tb_vehicle_gps_segment_mileage
+-- REORGANIZE PARTITION pfuture INTO (
+--     PARTITION p202701 VALUES LESS THAN (TO_DAYS('2027-02-01')),
+--     PARTITION pfuture VALUES LESS THAN MAXVALUE
+-- );
+
+-- 鍒犻櫎鍘嗗彶鍒嗗尯锛堥噴鏀剧┖闂达級
+-- ALTER TABLE tb_vehicle_gps_segment_mileage DROP PARTITION p202401;
+
+-- 鏌ョ湅鍒嗗尯鐘舵��
+-- SELECT * FROM information_schema.PARTITIONS 
+-- WHERE TABLE_SCHEMA = DATABASE() 
+--   AND TABLE_NAME = 'tb_vehicle_gps_segment_mileage';
diff --git a/sql/partition_vehicle_gps_segment_mileage.sql b/sql/partition_vehicle_gps_segment_mileage.sql
new file mode 100644
index 0000000..37b8105
--- /dev/null
+++ b/sql/partition_vehicle_gps_segment_mileage.sql
@@ -0,0 +1,304 @@
+-- ========================================
+-- GPS鍒嗘閲岀▼琛ㄥ垎鍖轰紭鍖栨柟妗�
+-- ========================================
+-- 鍔熻兘璇存槑锛�
+-- 1. 灏� tb_vehicle_gps_segment_mileage 琛ㄦ寜鏈堣繘琛屽垎鍖�
+-- 2. 鎻愰珮澶ф暟鎹噺涓嬬殑鏌ヨ鎬ц兘
+-- 3. 鏂逛究鍘嗗彶鏁版嵁褰掓。鍜屽垹闄�
+-- 
+-- 娉ㄦ剰浜嬮」锛�
+-- 1. 鎵ц姝よ剼鏈墠璇峰浠芥暟鎹簱锛�
+-- 2. 鏁版嵁閲忓ぇ鏃惰浆鎹㈣繃绋嬭緝鎱紝寤鸿鍦ㄤ笟鍔′綆宄版湡鎵ц
+-- 3. 鍒嗗尯鍚庝富閿拰鍞竴閿繀椤诲寘鍚垎鍖洪敭锛坰egment_start_time锛�
+-- ========================================
+
+-- 姝ラ1锛氬浠藉師琛ㄦ暟鎹�
+-- 寤鸿鍏堟墜鍔ㄦ墽琛岋細mysqldump -u鐢ㄦ埛鍚� -p 鏁版嵁搴撳悕 tb_vehicle_gps_segment_mileage > backup_segment_mileage.sql
+
+-- 姝ラ2锛氬垱寤烘柊鐨勫垎鍖鸿〃
+CREATE TABLE `tb_vehicle_gps_segment_mileage_new` (
+  `segment_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '鍒嗘ID',
+  `vehicle_id` bigint(20) NOT NULL COMMENT '杞﹁締ID',
+  `vehicle_no` varchar(20) DEFAULT NULL COMMENT '杞︾墝鍙�',
+  `segment_start_time` datetime NOT NULL COMMENT '鏃堕棿娈靛紑濮嬫椂闂�',
+  `segment_end_time` datetime NOT NULL COMMENT '鏃堕棿娈电粨鏉熸椂闂�',
+  `start_longitude` decimal(10,7) DEFAULT NULL COMMENT '璧风偣缁忓害',
+  `start_latitude` decimal(10,7) DEFAULT NULL COMMENT '璧风偣绾害',
+  `end_longitude` decimal(10,7) DEFAULT NULL COMMENT '缁堢偣缁忓害',
+  `end_latitude` decimal(10,7) DEFAULT NULL COMMENT '缁堢偣绾害',
+  `segment_distance` decimal(10,3) DEFAULT 0.000 COMMENT '娈佃窛绂�(鍏噷)',
+  `gps_point_count` int(11) DEFAULT 0 COMMENT 'GPS鐐规暟閲�',
+  `gps_ids` text COMMENT '鍏宠仈鐨凣PS璁板綍ID鍒楄〃锛堥�楀彿鍒嗛殧锛�',
+  `task_id` bigint(20) DEFAULT NULL COMMENT '鍏宠仈浠诲姟ID',
+  `task_code` varchar(50) DEFAULT NULL COMMENT '浠诲姟缂栧彿',
+  `calculate_method` varchar(20) DEFAULT 'tianditu' COMMENT '璁$畻鏂瑰紡(tianditu-澶╁湴鍥�/haversine-鐞冮潰璺濈)',
+  `create_time` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `update_time` datetime DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+  PRIMARY KEY (`segment_id`, `segment_start_time`),
+  -- 娉ㄦ剰锛氬垎鍖鸿〃鐨勫敮涓�閿繀椤诲寘鍚垎鍖洪敭
+  UNIQUE KEY `uk_vehicle_time` (`vehicle_id`, `segment_start_time`),
+  KEY `idx_vehicle_id` (`vehicle_id`),
+  KEY `idx_start_time` (`segment_start_time`),
+  KEY `idx_task_id` (`task_id`),
+  KEY `idx_vehicle_task` (`vehicle_id`, `task_id`),
+  KEY `idx_vehicle_date` (`vehicle_id`, `segment_start_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='杞﹁締GPS鍒嗘閲岀▼琛紙鍒嗗尯鐗堬級'
+PARTITION BY RANGE (TO_DAYS(segment_start_time)) (
+    -- 2024骞村垎鍖�
+    PARTITION p202401 VALUES LESS THAN (TO_DAYS('2024-02-01')),
+    PARTITION p202402 VALUES LESS THAN (TO_DAYS('2024-03-01')),
+    PARTITION p202403 VALUES LESS THAN (TO_DAYS('2024-04-01')),
+    PARTITION p202404 VALUES LESS THAN (TO_DAYS('2024-05-01')),
+    PARTITION p202405 VALUES LESS THAN (TO_DAYS('2024-06-01')),
+    PARTITION p202406 VALUES LESS THAN (TO_DAYS('2024-07-01')),
+    PARTITION p202407 VALUES LESS THAN (TO_DAYS('2024-08-01')),
+    PARTITION p202408 VALUES LESS THAN (TO_DAYS('2024-09-01')),
+    PARTITION p202409 VALUES LESS THAN (TO_DAYS('2024-10-01')),
+    PARTITION p202410 VALUES LESS THAN (TO_DAYS('2024-11-01')),
+    PARTITION p202411 VALUES LESS THAN (TO_DAYS('2024-12-01')),
+    PARTITION p202412 VALUES LESS THAN (TO_DAYS('2025-01-01')),
+    
+    -- 2025骞村垎鍖�
+    PARTITION p202501 VALUES LESS THAN (TO_DAYS('2025-02-01')),
+    PARTITION p202502 VALUES LESS THAN (TO_DAYS('2025-03-01')),
+    PARTITION p202503 VALUES LESS THAN (TO_DAYS('2025-04-01')),
+    PARTITION p202504 VALUES LESS THAN (TO_DAYS('2025-05-01')),
+    PARTITION p202505 VALUES LESS THAN (TO_DAYS('2025-06-01')),
+    PARTITION p202506 VALUES LESS THAN (TO_DAYS('2025-07-01')),
+    PARTITION p202507 VALUES LESS THAN (TO_DAYS('2025-08-01')),
+    PARTITION p202508 VALUES LESS THAN (TO_DAYS('2025-09-01')),
+    PARTITION p202509 VALUES LESS THAN (TO_DAYS('2025-10-01')),
+    PARTITION p202510 VALUES LESS THAN (TO_DAYS('2025-11-01')),
+    PARTITION p202511 VALUES LESS THAN (TO_DAYS('2025-12-01')),
+    PARTITION p202512 VALUES LESS THAN (TO_DAYS('2026-01-01')),
+    
+    -- 2026骞村垎鍖�
+    PARTITION p202601 VALUES LESS THAN (TO_DAYS('2026-02-01')),
+    PARTITION p202602 VALUES LESS THAN (TO_DAYS('2026-03-01')),
+    PARTITION p202603 VALUES LESS THAN (TO_DAYS('2026-04-01')),
+    PARTITION p202604 VALUES LESS THAN (TO_DAYS('2026-05-01')),
+    PARTITION p202605 VALUES LESS THAN (TO_DAYS('2026-06-01')),
+    PARTITION p202606 VALUES LESS THAN (TO_DAYS('2026-07-01')),
+    PARTITION p202607 VALUES LESS THAN (TO_DAYS('2026-08-01')),
+    PARTITION p202608 VALUES LESS THAN (TO_DAYS('2026-09-01')),
+    PARTITION p202609 VALUES LESS THAN (TO_DAYS('2026-10-01')),
+    PARTITION p202610 VALUES LESS THAN (TO_DAYS('2026-11-01')),
+    PARTITION p202611 VALUES LESS THAN (TO_DAYS('2026-12-01')),
+    PARTITION p202612 VALUES LESS THAN (TO_DAYS('2027-01-01')),
+    
+    -- 鏈潵鏁版嵁鍒嗗尯锛堝彲浠ュ绾�2027骞村強浠ュ悗鐨勬暟鎹級
+    PARTITION pfuture VALUES LESS THAN MAXVALUE
+);
+
+-- 姝ラ3锛氳縼绉绘暟鎹埌鏂拌〃
+-- 鏂瑰紡1锛氫竴娆℃�ц縼绉伙紙閫傜敤浜庢暟鎹噺杈冨皬锛屽100涓囦互涓嬶級
+-- INSERT INTO tb_vehicle_gps_segment_mileage_new SELECT * FROM tb_vehicle_gps_segment_mileage;
+
+-- 鏂瑰紡2锛氬垎鎵硅縼绉伙紙閫傜敤浜庡ぇ鏁版嵁閲忥紝鎺ㄨ崘锛�
+-- 鎸夋湀浠藉垎鎵硅縼绉伙紝鍑忓皯閿佽〃鏃堕棿
+-- 2024骞�1鏈�
+INSERT INTO tb_vehicle_gps_segment_mileage_new 
+SELECT * FROM tb_vehicle_gps_segment_mileage 
+WHERE segment_start_time >= '2024-01-01' AND segment_start_time < '2024-02-01';
+
+-- 2024骞�2鏈�
+INSERT INTO tb_vehicle_gps_segment_mileage_new 
+SELECT * FROM tb_vehicle_gps_segment_mileage 
+WHERE segment_start_time >= '2024-02-01' AND segment_start_time < '2024-03-01';
+
+-- 缁х画鎸夋湀杩佺Щ...锛堟牴鎹疄闄呮暟鎹儏鍐佃皟鏁达級
+-- 2024骞�3鏈堣嚦12鏈�
+INSERT INTO tb_vehicle_gps_segment_mileage_new 
+SELECT * FROM tb_vehicle_gps_segment_mileage 
+WHERE segment_start_time >= '2024-03-01' AND segment_start_time < '2025-01-01';
+
+-- 2025骞存暟鎹�
+INSERT INTO tb_vehicle_gps_segment_mileage_new 
+SELECT * FROM tb_vehicle_gps_segment_mileage 
+WHERE segment_start_time >= '2025-01-01' AND segment_start_time < '2026-01-01';
+
+-- 2026骞存暟鎹�
+INSERT INTO tb_vehicle_gps_segment_mileage_new 
+SELECT * FROM tb_vehicle_gps_segment_mileage 
+WHERE segment_start_time >= '2026-01-01';
+
+-- 姝ラ4锛氶獙璇佹暟鎹竴鑷存��
+-- 妫�鏌ュ師琛ㄥ拰鏂拌〃鐨勮褰曟暟
+SELECT 'Original Table' as table_name, COUNT(*) as record_count FROM tb_vehicle_gps_segment_mileage
+UNION ALL
+SELECT 'New Table' as table_name, COUNT(*) as record_count FROM tb_vehicle_gps_segment_mileage_new;
+
+-- 妫�鏌ュ悇鍒嗗尯鐨勬暟鎹噺
+SELECT 
+    PARTITION_NAME,
+    TABLE_ROWS,
+    AVG_ROW_LENGTH,
+    DATA_LENGTH,
+    INDEX_LENGTH,
+    CREATE_TIME
+FROM information_schema.PARTITIONS
+WHERE TABLE_SCHEMA = DATABASE()
+  AND TABLE_NAME = 'tb_vehicle_gps_segment_mileage_new'
+ORDER BY PARTITION_NAME;
+
+-- 姝ラ5锛氬垏鎹㈣〃鍚嶏紙璋ㄦ厧鎿嶄綔锛侊級
+-- 寤鸿鍦ㄤ笟鍔′綆宄版湡鎵ц锛屾暣涓搷浣滃簲鍦ㄤ簨鍔′腑瀹屾垚
+-- START TRANSACTION;
+
+-- 閲嶅懡鍚嶅師琛ㄤ负澶囦唤琛�
+RENAME TABLE tb_vehicle_gps_segment_mileage TO tb_vehicle_gps_segment_mileage_backup;
+
+-- 灏嗘柊琛ㄩ噸鍛藉悕涓烘寮忚〃
+RENAME TABLE tb_vehicle_gps_segment_mileage_new TO tb_vehicle_gps_segment_mileage;
+
+-- 濡傛灉涓�鍒囨甯革紝鎻愪氦浜嬪姟
+-- COMMIT;
+
+-- 濡傛灉鍑虹幇闂锛屽洖婊�
+-- ROLLBACK;
+
+-- 姝ラ6锛氶獙璇佸簲鐢ㄦ甯歌繍琛�
+-- 璇峰湪搴旂敤灞傛祴璇曚互涓嬪姛鑳斤細
+-- 1. GPS閲岀▼璁$畻鍔熻兘
+-- 2. 杞﹁締閲岀▼鏌ヨ
+-- 3. 浠诲姟閲岀▼缁熻
+-- 4. 鐩稿叧鎶ヨ〃鍔熻兘
+
+-- 姝ラ7锛氱‘璁ゆ棤璇悗鍒犻櫎澶囦唤琛紙鍙�夛紝寤鸿淇濈暀涓�娈垫椂闂达級
+-- DROP TABLE tb_vehicle_gps_segment_mileage_backup;
+
+-- ========================================
+-- 鍒嗗尯缁存姢鎿嶄綔锛堝畾鏈熸墽琛岋級
+-- ========================================
+
+-- 娣诲姞鏂版湀浠界殑鍒嗗尯锛堟瘡鏈堟垨姣忓搴︽墽琛屼竴娆★級
+-- 渚嬪锛氭坊鍔�2027骞�1鏈堢殑鍒嗗尯
+-- ALTER TABLE tb_vehicle_gps_segment_mileage
+-- REORGANIZE PARTITION pfuture INTO (
+--     PARTITION p202701 VALUES LESS THAN (TO_DAYS('2027-02-01')),
+--     PARTITION pfuture VALUES LESS THAN MAXVALUE
+-- );
+
+-- 鍒犻櫎鍘嗗彶鍒嗗尯锛堝綊妗f棫鏁版嵁锛岄噴鏀剧┖闂达級
+-- 渚嬪锛氬垹闄�2024骞�1鏈堢殑鏁版嵁锛堝垹闄ゅ墠璇风‘淇濆凡澶囦唤锛�
+-- ALTER TABLE tb_vehicle_gps_segment_mileage DROP PARTITION p202401;
+
+-- 鎴栬�呮竻绌哄垎鍖烘暟鎹絾淇濈暀鍒嗗尯缁撴瀯
+-- ALTER TABLE tb_vehicle_gps_segment_mileage TRUNCATE PARTITION p202401;
+
+-- ========================================
+-- 鎬ц兘浼樺寲寤鸿
+-- ========================================
+
+-- 1. 鏌ヨ鏃跺敖閲忓甫涓婃椂闂磋寖鍥存潯浠讹紝浠ュ厖鍒嗗埄鐢ㄥ垎鍖鸿鍓�
+-- 绀轰緥锛�
+-- SELECT * FROM tb_vehicle_gps_segment_mileage 
+-- WHERE segment_start_time >= '2025-01-01' 
+--   AND segment_start_time < '2025-02-01'
+--   AND vehicle_id = 123;
+
+-- 2. 瀹氭湡鍒嗘瀽琛ㄤ互浼樺寲鏌ヨ璁″垝
+-- ANALYZE TABLE tb_vehicle_gps_segment_mileage;
+
+-- 3. 瀹氭湡浼樺寲琛ㄤ互鍥炴敹绌洪棿
+-- OPTIMIZE TABLE tb_vehicle_gps_segment_mileage;
+
+-- 4. 鏌ョ湅鍒嗗尯浣跨敤鎯呭喌
+SELECT 
+    PARTITION_NAME as '鍒嗗尯鍚�',
+    PARTITION_METHOD as '鍒嗗尯鏂瑰紡',
+    PARTITION_EXPRESSION as '鍒嗗尯琛ㄨ揪寮�',
+    TABLE_ROWS as '璁板綍鏁�',
+    ROUND(DATA_LENGTH/1024/1024, 2) as '鏁版嵁澶у皬(MB)',
+    ROUND(INDEX_LENGTH/1024/1024, 2) as '绱㈠紩澶у皬(MB)',
+    PARTITION_COMMENT as '澶囨敞'
+FROM information_schema.PARTITIONS
+WHERE TABLE_SCHEMA = DATABASE()
+  AND TABLE_NAME = 'tb_vehicle_gps_segment_mileage'
+ORDER BY PARTITION_ORDINAL_POSITION;
+
+-- ========================================
+-- 鍒嗗尯鑷姩缁存姢鑴氭湰锛堝缓璁厤缃畾鏃朵换鍔★級
+-- ========================================
+
+DELIMITER $$
+
+CREATE PROCEDURE add_gps_segment_partition()
+BEGIN
+    DECLARE next_month_date DATE;
+    DECLARE partition_name VARCHAR(20);
+    DECLARE next_partition_date DATE;
+    
+    -- 璁$畻涓嬩釜鏈堢殑鏃ユ湡
+    SET next_month_date = DATE_ADD(CURDATE(), INTERVAL 2 MONTH);
+    SET next_month_date = DATE_FORMAT(next_month_date, '%Y-%m-01');
+    
+    -- 鐢熸垚鍒嗗尯鍚嶇О锛堜緥濡傦細p202701锛�
+    SET partition_name = CONCAT('p', DATE_FORMAT(next_month_date, '%Y%m'));
+    
+    -- 璁$畻涓嬩竴涓垎鍖虹殑杈圭晫鏃ユ湡
+    SET next_partition_date = DATE_ADD(next_month_date, INTERVAL 1 MONTH);
+    
+    -- 鍔ㄦ�佹坊鍔犲垎鍖�
+    SET @sql = CONCAT(
+        'ALTER TABLE tb_vehicle_gps_segment_mileage ',
+        'REORGANIZE PARTITION pfuture INTO (',
+        'PARTITION ', partition_name, ' VALUES LESS THAN (TO_DAYS(''', next_partition_date, ''')),',
+        'PARTITION pfuture VALUES LESS THAN MAXVALUE',
+        ')'
+    );
+    
+    PREPARE stmt FROM @sql;
+    EXECUTE stmt;
+    DEALLOCATE PREPARE stmt;
+    
+    SELECT CONCAT('鎴愬姛娣诲姞鍒嗗尯: ', partition_name, ', 杈圭晫鏃ユ湡: ', next_partition_date) as result;
+END$$
+
+DELIMITER ;
+
+-- 浣跨敤鏂规硶锛氭瘡鏈堟墽琛屼竴娆�
+-- CALL add_gps_segment_partition();
+
+-- ========================================
+-- 鍘嗗彶鏁版嵁褰掓。绛栫暐锛堝彲閫夛級
+-- ========================================
+
+-- 鏂规1锛氬鍑哄巻鍙插垎鍖哄埌褰掓。琛�
+-- CREATE TABLE tb_vehicle_gps_segment_mileage_archive LIKE tb_vehicle_gps_segment_mileage;
+-- ALTER TABLE tb_vehicle_gps_segment_mileage_archive REMOVE PARTITIONING;
+-- INSERT INTO tb_vehicle_gps_segment_mileage_archive 
+-- SELECT * FROM tb_vehicle_gps_segment_mileage PARTITION(p202401);
+
+-- 鏂规2锛氬鍑哄埌鏂囦欢
+-- SELECT * INTO OUTFILE '/tmp/gps_segment_202401.csv'
+-- FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
+-- LINES TERMINATED BY '\n'
+-- FROM tb_vehicle_gps_segment_mileage PARTITION(p202401);
+
+-- 鏂规3锛氬畾鏈熷垹闄よ秴杩嘚涓湀鐨勫巻鍙叉暟鎹�
+-- 渚嬪锛氬垹闄�12涓湀涔嬪墠鐨勬暟鎹�
+-- ALTER TABLE tb_vehicle_gps_segment_mileage 
+-- DROP PARTITION p202401;
+
+-- ========================================
+-- 鐩戞帶鍜屽憡璀︼紙寤鸿锛�
+-- ========================================
+
+-- 鐩戞帶鍚勫垎鍖虹殑鏁版嵁閲忓闀�
+CREATE VIEW v_gps_segment_partition_stats AS
+SELECT 
+    PARTITION_NAME as partition_name,
+    TABLE_ROWS as row_count,
+    ROUND(DATA_LENGTH/1024/1024, 2) as data_size_mb,
+    ROUND(INDEX_LENGTH/1024/1024, 2) as index_size_mb,
+    ROUND((DATA_LENGTH + INDEX_LENGTH)/1024/1024, 2) as total_size_mb,
+    CREATE_TIME as create_time,
+    UPDATE_TIME as update_time
+FROM information_schema.PARTITIONS
+WHERE TABLE_SCHEMA = DATABASE()
+  AND TABLE_NAME = 'tb_vehicle_gps_segment_mileage'
+ORDER BY PARTITION_ORDINAL_POSITION;
+
+-- 鏌ョ湅鍒嗗尯缁熻
+-- SELECT * FROM v_gps_segment_partition_stats;
diff --git "a/sql/\345\210\206\345\214\272\344\274\230\345\214\226\346\226\271\346\241\210\350\257\264\346\230\216.md" "b/sql/\345\210\206\345\214\272\344\274\230\345\214\226\346\226\271\346\241\210\350\257\264\346\230\216.md"
new file mode 100644
index 0000000..6a39021
--- /dev/null
+++ "b/sql/\345\210\206\345\214\272\344\274\230\345\214\226\346\226\271\346\241\210\350\257\264\346\230\216.md"
@@ -0,0 +1,293 @@
+# GPS鍒嗘閲岀▼琛ㄥ垎鍖轰紭鍖栨柟妗�
+
+## 涓�銆侀棶棰樺垎鏋�
+
+### 褰撳墠鎯呭喌
+- **琛ㄥ悕**: `tb_vehicle_gps_segment_mileage`
+- **鏁版嵁鐗圭偣**: 鎸�5鍒嗛挓鏃堕棿娈电粺璁PS閲岀▼锛屾暟鎹噺澧為暱蹇�
+- **涓昏闂**: 
+  - 鏁版嵁閲忓ぇ瀵艰嚧鏌ヨ鍙樻參
+  - 绱㈠紩鏁堢巼闄嶄綆
+  - 鍘嗗彶鏁版嵁闅句互娓呯悊
+
+### 鍒嗗尯浼樺寲鏀剁泭
+鉁� **鏌ヨ鎬ц兘鎻愬崌**: 閫氳繃鍒嗗尯瑁佸壀锛屾煡璇㈠彧鎵弿鐩稿叧鍒嗗尯锛屾�ц兘鎻愬崌50%-80%  
+鉁� **缁存姢绠�鍖�**: 鎸夋湀鍒嗗尯锛屽彲浠ュ揩閫熷垹闄ゆ垨褰掓。鍘嗗彶鏁版嵁  
+鉁� **瀛樺偍浼樺寲**: 渚夸簬鏁版嵁褰掓。锛岄噴鏀剧鐩樼┖闂�  
+鉁� **骞跺彂浼樺寲**: 涓嶅悓鍒嗗尯鍙互骞惰鎿嶄綔锛屽噺灏戦攣鍐茬獊
+
+---
+
+## 浜屻�佸垎鍖烘柟妗堣璁�
+
+### 1. 鍒嗗尯绛栫暐
+- **鍒嗗尯绫诲瀷**: RANGE 鍒嗗尯锛堟寜鏃堕棿鑼冨洿锛�
+- **鍒嗗尯閿�**: `segment_start_time`锛堟椂闂存寮�濮嬫椂闂达級
+- **鍒嗗尯绮掑害**: 鎸夋湀鍒嗗尯
+- **淇濈暀鍛ㄦ湡**: 寤鸿淇濈暀鏈�杩�12-24涓湀鏁版嵁
+
+### 2. 鍒嗗尯缁撴瀯
+```
+2024骞�: p202401, p202402, ..., p202412 (12涓垎鍖�)
+2025骞�: p202501, p202502, ..., p202512 (12涓垎鍖�)
+2026骞�: p202601, p202602, ..., p202612 (12涓垎鍖�)
+鏈潵: pfuture (瀹圭撼鎵�鏈夋湭鏉ユ暟鎹�)
+```
+
+### 3. 鍏抽敭鍙樻洿鐐�
+鈿狅笍 **閲嶈**: 鍒嗗尯琛ㄧ殑涓婚敭鍜屽敮涓�閿繀椤诲寘鍚垎鍖洪敭
+
+**鍘熻〃缁撴瀯**:
+```sql
+PRIMARY KEY (`segment_id`),
+UNIQUE KEY `uk_vehicle_time` (`vehicle_id`, `segment_start_time`)
+```
+
+**鏂拌〃缁撴瀯**:
+```sql
+PRIMARY KEY (`segment_id`, `segment_start_time`),  -- 涓婚敭鍖呭惈鍒嗗尯閿�
+UNIQUE KEY `uk_vehicle_time` (`vehicle_id`, `segment_start_time`)  -- 宸插寘鍚垎鍖洪敭
+```
+
+---
+
+## 涓夈�佹墽琛屾楠�
+
+### 姝ラ1: 鏁版嵁澶囦唤锛堝繀椤伙紒锛�
+```bash
+# 澶囦唤鏁翠釜鏁版嵁搴�
+mysqldump -u鐢ㄦ埛鍚� -p 鏁版嵁搴撳悕 > backup_$(date +%Y%m%d).sql
+
+# 鎴栧彧澶囦唤鍗曡〃
+mysqldump -u鐢ㄦ埛鍚� -p 鏁版嵁搴撳悕 tb_vehicle_gps_segment_mileage > backup_segment_mileage_$(date +%Y%m%d).sql
+```
+
+### 姝ラ2: 鏌ョ湅褰撳墠鏁版嵁閲�
+```sql
+-- 鏌ョ湅鎬昏褰曟暟
+SELECT COUNT(*) as total_records FROM tb_vehicle_gps_segment_mileage;
+
+-- 鏌ョ湅鎸夋湀鍒嗗竷
+SELECT 
+    DATE_FORMAT(segment_start_time, '%Y-%m') as month,
+    COUNT(*) as record_count,
+    ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM tb_vehicle_gps_segment_mileage), 2) as percentage
+FROM tb_vehicle_gps_segment_mileage
+GROUP BY DATE_FORMAT(segment_start_time, '%Y-%m')
+ORDER BY month;
+
+-- 鏌ョ湅琛ㄥぇ灏�
+SELECT 
+    TABLE_NAME,
+    ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS size_mb,
+    TABLE_ROWS
+FROM information_schema.TABLES
+WHERE TABLE_SCHEMA = DATABASE()
+  AND TABLE_NAME = 'tb_vehicle_gps_segment_mileage';
+```
+
+### 姝ラ3: 鎵ц鍒嗗尯鑴氭湰
+```sql
+-- 鍦ㄤ笟鍔′綆宄版湡鎵ц partition_vehicle_gps_segment_mileage.sql
+source d:/project/鎬ユ晳杞繍/code/Api/RuoYi-Vue-master/sql/partition_vehicle_gps_segment_mileage.sql
+```
+
+**鎴栧垎姝ユ墽琛�**:
+1. 鍒涘缓鏂板垎鍖鸿〃锛坄tb_vehicle_gps_segment_mileage_new`锛�
+2. 鍒嗘壒杩佺Щ鏁版嵁
+3. 楠岃瘉鏁版嵁涓�鑷存��
+4. 鍒囨崲琛ㄥ悕
+
+### 姝ラ4: 楠岃瘉搴旂敤鍔熻兘
+娴嬭瘯浠ヤ笅鍔熻兘鏄惁姝e父锛�
+- 鉁� GPS閲岀▼璁$畻浠诲姟
+- 鉁� 杞﹁締閲岀▼鏌ヨ
+- 鉁� 浠诲姟閲岀▼缁熻
+- 鉁� 閲岀▼鎶ヨ〃
+
+### 姝ラ5: 鍒犻櫎澶囦唤琛紙鍙�夛級
+```sql
+-- 纭杩愯姝e父鍚庯紝鍙互鍒犻櫎澶囦唤琛紙寤鸿淇濈暀1-2鍛級
+DROP TABLE tb_vehicle_gps_segment_mileage_backup;
+```
+
+---
+
+## 鍥涖�佹棩甯哥淮鎶ゆ搷浣�
+
+### 1. 娣诲姞鏂版湀浠藉垎鍖猴紙姣忔湀鎵ц锛�
+```sql
+-- 鎵嬪姩娣诲姞2027骞�2鏈堝垎鍖�
+ALTER TABLE tb_vehicle_gps_segment_mileage
+REORGANIZE PARTITION pfuture INTO (
+    PARTITION p202702 VALUES LESS THAN (TO_DAYS('2027-03-01')),
+    PARTITION pfuture VALUES LESS THAN MAXVALUE
+);
+
+-- 鎴栦娇鐢ㄥ瓨鍌ㄨ繃绋嬭嚜鍔ㄦ坊鍔�
+CALL add_gps_segment_partition();
+```
+
+### 2. 鍒犻櫎鍘嗗彶鍒嗗尯锛堥噴鏀剧┖闂达級
+```sql
+-- 鏂瑰紡1: 鐩存帴鍒犻櫎鍒嗗尯锛堟暟鎹笉鍙仮澶嶏級
+ALTER TABLE tb_vehicle_gps_segment_mileage DROP PARTITION p202401;
+
+-- 鏂瑰紡2: 褰掓。鍚庡垹闄�
+-- 鍏堝鍑烘暟鎹埌褰掓。琛�
+CREATE TABLE tb_vehicle_gps_segment_mileage_archive_202401 
+SELECT * FROM tb_vehicle_gps_segment_mileage PARTITION(p202401);
+
+-- 鐒跺悗鍒犻櫎鍒嗗尯
+ALTER TABLE tb_vehicle_gps_segment_mileage DROP PARTITION p202401;
+```
+
+### 3. 鏌ョ湅鍒嗗尯鐘舵��
+```sql
+-- 鏌ョ湅鎵�鏈夊垎鍖轰俊鎭�
+SELECT * FROM v_gps_segment_partition_stats;
+
+-- 鎴栫洿鎺ユ煡璇�
+SELECT 
+    PARTITION_NAME as '鍒嗗尯鍚�',
+    TABLE_ROWS as '璁板綍鏁�',
+    ROUND(DATA_LENGTH/1024/1024, 2) as '鏁版嵁(MB)',
+    ROUND(INDEX_LENGTH/1024/1024, 2) as '绱㈠紩(MB)'
+FROM information_schema.PARTITIONS
+WHERE TABLE_SCHEMA = DATABASE()
+  AND TABLE_NAME = 'tb_vehicle_gps_segment_mileage'
+ORDER BY PARTITION_ORDINAL_POSITION;
+```
+
+### 4. 浼樺寲鍒嗗尯
+```sql
+-- 鍒嗘瀽琛紙鏇存柊缁熻淇℃伅锛�
+ANALYZE TABLE tb_vehicle_gps_segment_mileage;
+
+-- 浼樺寲琛紙鍥炴敹纰庣墖绌洪棿锛�
+OPTIMIZE TABLE tb_vehicle_gps_segment_mileage;
+```
+
+---
+
+## 浜斻�佹煡璇紭鍖栧缓璁�
+
+### 鉁� 濂界殑鏌ヨ锛堝埄鐢ㄥ垎鍖鸿鍓級
+```sql
+-- 绀轰緥1: 甯︽椂闂磋寖鍥寸殑鏌ヨ
+SELECT * FROM tb_vehicle_gps_segment_mileage 
+WHERE segment_start_time >= '2025-01-01' 
+  AND segment_start_time < '2025-02-01'
+  AND vehicle_id = 123;
+
+-- 绀轰緥2: 鎸夋湀缁熻
+SELECT 
+    DATE_FORMAT(segment_start_time, '%Y-%m') as month,
+    SUM(segment_distance) as total_distance
+FROM tb_vehicle_gps_segment_mileage
+WHERE segment_start_time >= '2025-01-01' 
+  AND segment_start_time < '2025-12-31'
+GROUP BY DATE_FORMAT(segment_start_time, '%Y-%m');
+```
+
+### 鉂� 涓嶅ソ鐨勬煡璇紙鍏ㄨ〃鎵弿锛�
+```sql
+-- 缂哄皯鏃堕棿鏉′欢锛屼細鎵弿鎵�鏈夊垎鍖�
+SELECT * FROM tb_vehicle_gps_segment_mileage 
+WHERE vehicle_id = 123;
+
+-- 搴旇鏀逛负锛�
+SELECT * FROM tb_vehicle_gps_segment_mileage 
+WHERE vehicle_id = 123
+  AND segment_start_time >= DATE_SUB(NOW(), INTERVAL 30 DAY);
+```
+
+---
+
+## 鍏�佺洃鎺у拰鍛婅
+
+### 1. 鐩戞帶鎸囨爣
+- 鍚勫垎鍖虹殑鏁版嵁閲�
+- 琛ㄥ拰绱㈠紩鐨勫ぇ灏�
+- 鏈�鏂板垎鍖烘槸鍚︽帴杩戞弧
+- 鏌ヨ鎬ц兘瀵规瘮锛堝垎鍖哄墠鍚庯級
+
+### 2. 瀹氭椂浠诲姟寤鸿
+```bash
+# 姣忔湀1鍙峰噷鏅�1鐐硅嚜鍔ㄦ坊鍔犳柊鍒嗗尯
+0 1 1 * * mysql -u鐢ㄦ埛鍚� -p瀵嗙爜 鏁版嵁搴撳悕 -e "CALL add_gps_segment_partition();"
+
+# 姣忓搴﹀垹闄�12涓湀涔嬪墠鐨勫巻鍙插垎鍖�
+0 2 1 1,4,7,10 * /path/to/cleanup_old_partitions.sh
+```
+
+---
+
+## 涓冦�佸洖婊氭柟妗�
+
+濡傛灉鍒嗗尯鍚庡嚭鐜伴棶棰橈紝鍙互蹇�熷洖婊氾細
+
+```sql
+-- 1. 鍋滄搴旂敤鍐欏叆
+
+-- 2. 鎭㈠鍘熻〃
+RENAME TABLE tb_vehicle_gps_segment_mileage TO tb_vehicle_gps_segment_mileage_failed;
+RENAME TABLE tb_vehicle_gps_segment_mileage_backup TO tb_vehicle_gps_segment_mileage;
+
+-- 3. 鍚屾鍒囨崲鏈熼棿鐨勬柊鏁版嵁锛堝鏋滄湁锛�
+INSERT INTO tb_vehicle_gps_segment_mileage 
+SELECT * FROM tb_vehicle_gps_segment_mileage_failed
+WHERE create_time > (SELECT MAX(create_time) FROM tb_vehicle_gps_segment_mileage);
+
+-- 4. 閲嶅惎搴旂敤
+```
+
+---
+
+## 鍏�佹�ц兘瀵规瘮锛堥鏈燂級
+
+### 鍒嗗尯鍓�
+- 鏌ヨ鏈�杩�1鏈堟暟鎹�: ~5-10绉�
+- 鏌ヨ鏈�杩�3鏈堟暟鎹�: ~15-30绉�
+- 琛ㄥぇ灏�: 鎸佺画澧為暱锛岀储寮曟晥鐜囬檷浣�
+
+### 鍒嗗尯鍚�
+- 鏌ヨ鏈�杩�1鏈堟暟鎹�: ~1-2绉掞紙鎻愬崌70%-80%锛�
+- 鏌ヨ鏈�杩�3鏈堟暟鎹�: ~3-6绉掞紙鎻愬崌70%-80%锛�
+- 琛ㄧ淮鎶�: 鍙寜鏈堟竻鐞嗭紝绌洪棿鍙帶
+
+---
+
+## 涔濄�佸父瑙侀棶棰�
+
+### Q1: 鍒嗗尯浼氬奖鍝嶅簲鐢ㄤ唬鐮佸悧锛�
+**A**: 涓嶄細銆傚垎鍖哄搴旂敤閫忔槑锛孲QL璇彞涓嶉渶瑕佷慨鏀广��
+
+### Q2: 鍙互鍦ㄧ嚎杞崲鍚楋紵
+**A**: MySQL 5.7+ 鍙互鍦ㄧ嚎杞崲锛屼絾寤鸿鍦ㄤ綆宄版湡鎵ц锛屽ぇ琛ㄥ彲鑳介渶瑕佽緝闀挎椂闂淬��
+
+### Q3: 鍒嗗尯鍚庤兘鍥炲埌闈炲垎鍖鸿〃鍚楋紵
+**A**: 鍙互锛屼娇鐢� `ALTER TABLE ... REMOVE PARTITIONING;`
+
+### Q4: 涓婚敭蹇呴』鍖呭惈鍒嗗尯閿悧锛�
+**A**: 鏄殑锛岃繖鏄疢ySQL鍒嗗尯琛ㄧ殑闄愬埗銆�
+
+### Q5: 濡備綍纭畾淇濈暀澶氫箙鐨勫巻鍙叉暟鎹紵
+**A**: 鏍规嵁涓氬姟闇�姹傚拰瀛樺偍瀹归噺锛屽缓璁繚鐣�12-24涓湀锛屾洿鏃╃殑鏁版嵁褰掓。鍒板喎瀛樺偍銆�
+
+---
+
+## 鍗併�佽仈绯绘敮鎸�
+
+濡傛灉鎵ц杩囩▼涓亣鍒伴棶棰橈紝璇凤細
+1. 妫�鏌ラ敊璇棩蹇�: `/var/log/mysql/error.log`
+2. 鏌ョ湅鎱㈡煡璇㈡棩蹇楋紝瀵规瘮鎬ц兘
+3. 纭繚鏈夊畬鏁村浠�
+4. 蹇呰鏃惰仈绯籇BA鎴栨妧鏈敮鎸�
+
+---
+
+**鏈�鍚庢彁閱�**: 
+鈿狅笍 鎵ц鍓嶅姟蹇呭浠斤紒
+鈿狅笍 閫夋嫨涓氬姟浣庡嘲鏈熸墽琛岋紒
+鈿狅笍 鍑嗗濂藉洖婊氭柟妗堬紒

--
Gitblit v1.9.1