From 73905dea456af423049753fff10a853d7394ece7 Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期日, 12 四月 2026 20:40:25 +0800
Subject: [PATCH] feat(WMS): 新增dispatchTasksToWCS.vue批量下发弹窗组件 - 工具栏按钮触发弹窗 - 表格展示选中任务,可编辑地址和优先级 - 非可下发状态任务行标红且不可编辑 - 显示下发失败任务列表

---
 Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs                |   34 
 Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/gridBodyExtension.vue      |  329 +++++
 Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Outbound.cs      |  140 ++
 Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs                      |   94 +
 Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Inbound.cs       |  210 +++
 Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_DTO/TaskInfo/ReceiveTaskResultDto.cs          |   54 
 Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs |   11 
 /dev/null                                                                           |  960 ---------------
 Code/docs/superpowers/specs/2026-04-12-manual-dispatch-tasks-to-wcs-design.md       |  154 ++
 Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_AutoOutbound.cs  |  175 ++
 Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Robot.cs         |  147 ++
 Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs                 |    7 
 Code/docs/superpowers/plans/2026-04-12-manual-dispatch-tasks-to-wcs-plan.md         |  723 +++++++++++
 Code/.omc/state/idle-notif-cooldown.json                                            |    3 
 Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js                           |   18 
 Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Manual.cs        |  328 +++++
 Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Relocation.cs    |   61 
 Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/ReceiveTaskResultDto.cs                 |   54 
 Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_TaskStatus.cs    |   39 
 Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Tray.cs          |  197 +++
 20 files changed, 2,775 insertions(+), 963 deletions(-)

diff --git a/Code/.omc/state/idle-notif-cooldown.json b/Code/.omc/state/idle-notif-cooldown.json
new file mode 100644
index 0000000..f247f90
--- /dev/null
+++ b/Code/.omc/state/idle-notif-cooldown.json
@@ -0,0 +1,3 @@
+{
+  "lastSentAt": "2026-04-12T12:35:52.350Z"
+}
\ No newline at end of file
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_DTO/TaskInfo/ReceiveTaskResultDto.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_DTO/TaskInfo/ReceiveTaskResultDto.cs
new file mode 100644
index 0000000..6dd01cd
--- /dev/null
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_DTO/TaskInfo/ReceiveTaskResultDto.cs
@@ -0,0 +1,54 @@
+namespace WIDESEAWCS_DTO.TaskInfo
+{
+    /// <summary>
+    /// WCS鎺ユ敹WMS浠诲姟鐨勭粨鏋淒to
+    /// </summary>
+    public class ReceiveTaskResultDto
+    {
+        /// <summary>
+        /// 鏄惁鎴愬姛
+        /// </summary>
+        public bool Success { get; set; }
+
+        /// <summary>
+        /// 娑堟伅
+        /// </summary>
+        public string Message { get; set; }
+
+        /// <summary>
+        /// 鏂板缓鐨勪换鍔℃暟閲�
+        /// </summary>
+        public int CreatedCount { get; set; }
+
+        /// <summary>
+        /// 閲嶅/宸插瓨鍦ㄧ殑浠诲姟鍒楄〃锛堝彧鍖呭惈浠诲姟鍙峰拰鎵樼洏鍙凤級
+        /// </summary>
+        public List<DuplicateTaskDto> DuplicateTasks { get; set; }
+    }
+
+    /// <summary>
+    /// 閲嶅浠诲姟淇℃伅Dto
+    /// </summary>
+    public class DuplicateTaskDto
+    {
+        /// <summary>
+        /// 浠诲姟鍙�
+        /// </summary>
+        public int TaskNum { get; set; }
+
+        /// <summary>
+        /// 鎵樼洏鍙�
+        /// </summary>
+        public string PalletCode { get; set; }
+
+        /// <summary>
+        /// 浠诲姟绫诲瀷
+        /// </summary>
+        public int TaskType { get; set; }
+
+        /// <summary>
+        /// 褰撳墠鐘舵��
+        /// </summary>
+        public int TaskStatus { get; set; }
+    }
+}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs
index c6c5522..1032614 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs
@@ -655,8 +655,42 @@
             WebResponseContent content = new WebResponseContent();
             try
             {
+                // 鏀堕泦閲嶅浠诲姟鐨勪俊鎭�
+                var duplicateTasks = new List<DuplicateTaskDto>();
+                // 鍊掑簭閬嶅巻锛屽畨鍏ㄥ垹闄ゅ苟鏀堕泦琚Щ闄ょ殑椤�
+                for (int i = taskDTOs.Count - 1; i >= 0; i--)
+                {
+                    var item = taskDTOs[i];
+                    var exists = BaseDal.QueryFirst(x => x.TaskNum == item.TaskNum || x.PalletCode == item.PalletCode);
+                    if (exists != null)
+                    {
+                        duplicateTasks.Add(new DuplicateTaskDto
+                        {
+                            TaskNum = exists.TaskNum,
+                            PalletCode = exists.PalletCode,
+                            TaskType = exists.TaskType,
+                            TaskStatus = exists.TaskStatus
+                        });
+                        taskDTOs.RemoveAt(i);
+                    }
+                }
+
                 // 璋冪敤 ReceiveWMSTask 鍒涘缓 WCS 浠诲姟
                 content = ReceiveWMSTask(taskDTOs);
+
+                // 濡傛灉鏈夐噸澶嶄换鍔★紝淇敼杩斿洖缁撴灉
+                if (duplicateTasks.Count > 0 && content.Status)
+                {
+                    var result = new ReceiveTaskResultDto
+                    {
+                        Success = true,
+                        Message = content.Message + $"锛屽叾涓瓄duplicateTasks.Count}涓换鍔″湪WCS涓凡瀛樺湪",
+                        CreatedCount = taskDTOs.Count,
+                        DuplicateTasks = duplicateTasks
+                    };
+                    content.Data = result;
+                }
+
                 return content;
             }
             catch (Exception ex)
diff --git a/Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/gridBodyExtension.vue b/Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/gridBodyExtension.vue
new file mode 100644
index 0000000..cf4fe98
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/gridBodyExtension.vue
@@ -0,0 +1,329 @@
+<template>
+  <div>
+    <!-- 鎵嬪姩鍒涘缓浠诲姟寮圭獥 -->
+    <vol-box v-model="showManualCreate" :lazy="true" width="500px" :padding="15" title="鎵嬪姩鍒涘缓浠诲姟">
+      <el-form :model="manualFormData" ref="form" label-width="100px">
+        <el-form-item label="浠诲姟绫诲瀷" prop="taskType" required>
+          <el-select v-model="manualFormData.taskType" placeholder="璇烽�夋嫨浠诲姟绫诲瀷">
+            <el-option label="鍏ュ簱" value="鍏ュ簱"></el-option>
+            <el-option label="鍑哄簱" value="鍑哄簱"></el-option>
+            <el-option label="绉诲簱" value="绉诲簱"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="璧风偣鍦板潃" prop="sourceAddress" required>
+          <el-input v-model="manualFormData.sourceAddress" placeholder="璇疯緭鍏ヨ捣鐐瑰湴鍧�"></el-input>
+        </el-form-item>
+        <el-form-item label="缁堢偣鍦板潃" prop="targetAddress" required>
+          <el-input v-model="manualFormData.targetAddress" placeholder="璇疯緭鍏ョ粓鐐瑰湴鍧�"></el-input>
+        </el-form-item>
+        <el-form-item label="鏉$爜" prop="barcode" required>
+          <el-input v-model="manualFormData.barcode" placeholder="璇疯緭鍏ユ潯鐮�"></el-input>
+        </el-form-item>
+        <el-form-item label="浠撳簱ID" prop="warehouseId" required>
+          <el-input v-model="manualFormData.warehouseId" placeholder="璇疯緭鍏ヤ粨搴揑D"></el-input>
+        </el-form-item>
+        <el-form-item label="浼樺厛绾�">
+          <el-input v-model="manualFormData.grade" readonly></el-input>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" size="small" @click="submitManualCreate">纭畾</el-button>
+        <el-button type="danger" size="small" @click="showManualCreate = false">鍏抽棴</el-button>
+      </template>
+    </vol-box>
+
+    <!-- 鎵嬪姩涓嬪彂浠诲姟寮圭獥 -->
+    <vol-box v-model="showDispatch" :lazy="true" width="900px" :padding="15" title="鎵嬪姩涓嬪彂浠诲姟鍒� WCS">
+      <div v-if="dispatchRows.length > 0" class="dispatch-info">
+        宸查�変换鍔℃暟: <span class="count">{{ dispatchRows.length }}</span> 涓�
+      </div>
+
+      <el-table :data="dispatchTableData" border style="width: 100%; margin-top: 10px" max-height="400">
+        <el-table-column prop="taskNum" label="浠诲姟鍙�" width="120"></el-table-column>
+        <el-table-column prop="palletCode" label="鎵樼洏鍙�" width="140">
+          <template v-slot="{ row }">
+            <el-input v-if="row && row.editable" v-model="row.palletCode" size="small" placeholder="璇疯緭鍏�"></el-input>
+            <span v-else>{{ row ? row.palletCode : '' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="sourceAddress" label="璧风偣鍦板潃" width="160">
+          <template v-slot="{ row }">
+            <el-input v-if="row && row.editable" v-model="row.sourceAddress" size="small" placeholder="璇疯緭鍏�"></el-input>
+            <span v-else>{{ row ? row.sourceAddress : '' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="targetAddress" label="缁堢偣鍦板潃" width="160">
+          <template v-slot="{ row }">
+            <el-input v-if="row && row.editable" v-model="row.targetAddress" size="small" placeholder="璇疯緭鍏�"></el-input>
+            <span v-else>{{ row ? row.targetAddress : '' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="grade" label="浼樺厛绾�" width="100">
+          <template v-slot="{ row }">
+            <el-input-number v-if="row && row.editable" v-model="row.grade" :min="1" :max="99" size="small" style="width: 80px"></el-input-number>
+            <span v-else>{{ row ? row.grade : '' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="statusName" label="鐘舵��" width="120">
+          <template v-slot="{ row }">
+            <span :class="{ 'status-error': row && !row.editable }">{{ row ? row.statusName : '' }}</span>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div v-if="dispatchFailResults && dispatchFailResults.length > 0" class="fail-results">
+        <div class="fail-title">涓嬪彂澶辫触浠诲姟锛�</div>
+        <el-table :data="dispatchFailResults" border style="width: 100%; margin-top: 10px" max-height="200">
+          <el-table-column prop="taskNum" label="浠诲姟鍙�" width="120"></el-table-column>
+          <el-table-column prop="errorMessage" label="澶辫触鍘熷洜"></el-table-column>
+        </el-table>
+      </div>
+
+      <template #footer>
+        <el-button type="primary" size="small" @click="submitDispatch" :loading="dispatchLoading">纭涓嬪彂</el-button>
+        <el-button type="danger" size="small" @click="showDispatch = false">鍙栨秷</el-button>
+      </template>
+    </vol-box>
+  </div>
+</template>
+
+<script>
+import VolBox from "@/components/basic/VolBox.vue";
+
+export default {
+  components: { VolBox },
+  emits: ["parentCall"],
+  data() {
+    return {
+      // 鎵嬪姩鍒涘缓浠诲姟
+      showManualCreate: false,
+      manualFormData: {
+        taskType: "",
+        sourceAddress: "",
+        targetAddress: "",
+        barcode: "",
+        warehouseId: "",
+        grade: 1,
+      },
+      // 鎵嬪姩涓嬪彂浠诲姟
+      showDispatch: false,
+      dispatchLoading: false,
+      dispatchRows: [],
+      dispatchTableData: [],
+      dispatchFailResults: [],
+      dispatchOriginalData: [],
+    };
+  },
+  methods: {
+    // 鎵嬪姩鍒涘缓浠诲姟 - 鎵撳紑
+    open() {
+      this.showManualCreate = true;
+      this.resetManualForm();
+    },
+    resetManualForm() {
+      this.manualFormData = {
+        taskType: "",
+        sourceAddress: "",
+        targetAddress: "",
+        barcode: "",
+        warehouseId: "",
+        grade: 1,
+      };
+    },
+    submitManualCreate() {
+      if (!this.manualFormData.taskType) return this.$message.error("璇烽�夋嫨浠诲姟绫诲瀷");
+      if (!this.manualFormData.sourceAddress) return this.$message.error("璇疯緭鍏ヨ捣鐐瑰湴鍧�");
+      if (!this.manualFormData.targetAddress) return this.$message.error("璇疯緭鍏ョ粓鐐瑰湴鍧�");
+      if (!this.manualFormData.barcode) return this.$message.error("璇疯緭鍏ユ潯鐮�");
+      if (!this.manualFormData.warehouseId) return this.$message.error("璇疯緭鍏ヤ粨搴揑D");
+
+      this.http
+        .post("/api/Task/CreateManualTask", this.manualFormData, "鏁版嵁澶勭悊涓�...")
+        .then((res) => {
+          if (!res.status) return this.$message.error(res.message);
+          this.$message.success("浠诲姟鍒涘缓鎴愬姛");
+          this.showManualCreate = false;
+          this.$emit("parentCall", ($vue) => { $vue.refresh(); });
+        });
+    },
+    // 鎵嬪姩涓嬪彂浠诲姟 - 鎵撳紑
+    openDispatch(rows) {
+      console.log('openDispatch called with rows:', rows);
+      console.log('rows type:', typeof rows, Array.isArray(rows));
+      // 纭繚鏄暟缁�
+      if (!rows) {
+        this.dispatchRows = [];
+      } else if (Array.isArray(rows)) {
+        this.dispatchRows = rows;
+      } else {
+        // 濡傛灉鏄崟涓璞★紝鍖呰鎴愭暟缁�
+        this.dispatchRows = [rows];
+      }
+      console.log('dispatchRows length:', this.dispatchRows.length);
+      this.dispatchFailResults = [];
+      // 鍏堝垵濮嬪寲鏁版嵁锛屽啀鏄剧ず寮圭獥
+      this.initDispatchTableData();
+      this.showDispatch = true;
+    },
+    initDispatchTableData() {
+      console.log('initDispatchTableData dispatchRows:', this.dispatchRows);
+      console.log('dispatchRows isArray:', Array.isArray(this.dispatchRows), 'length:', this.dispatchRows?.length);
+
+      // 纭繚 dispatchRows 鏄暟缁�
+      if (!this.dispatchRows || !Array.isArray(this.dispatchRows)) {
+        console.warn('dispatchRows 涓嶆槸鏈夋晥鏁扮粍锛岄噸缃负绌烘暟缁�');
+        this.dispatchTableData = [];
+        return;
+      }
+
+      const statusNames = {
+        'inbound_200': '鍏ュ簱鏂板崟',
+        'outbound_100': '鍑哄簱鏂板崟',
+        'relocation_300': '绉诲簱鏂板崟',
+        'outbound_110': '鍫嗗灈鏈哄嚭搴撴墽琛屼腑',
+        'outbound_115': '鍫嗗灈鏈哄嚭搴撳畬鎴�',
+        'inbound_220': '杈撻�佺嚎鍏ュ簱鎵ц涓�',
+        'inbound_230': '鍫嗗灈鏈哄叆搴撴墽琛屼腑',
+        'inbound_290': '鍏ュ簱浠诲姟瀹屾垚',
+        'outbound_120': '杈撻�佺嚎鍑哄簱鎵ц涓�',
+        'outbound_125': '杈撻�佺嚎鍑哄簱瀹屾垚',
+        'outbound_200': '鍑哄簱浠诲姟瀹屾垚',
+        'relocation_310': '鍫嗗灈鏈虹Щ搴撴墽琛屼腑'
+      };
+
+      const result = [];
+      const original = [];
+      let filteredCount = 0; // 璁板綍琚繃婊ょ殑浠诲姟鏁伴噺
+      for (const row of this.dispatchRows) {
+        console.log('Processing row:', row);
+        // Handle possible field name differences (camelCase vs PascalCase)
+        const taskId = row.taskId ?? row.Id ?? row.id ?? 0;
+        const taskType = row.taskType ?? row.TaskType ?? row.task_type ?? 0;
+        const taskStatus = row.taskStatus ?? row.TaskStatus ?? row.task_status ?? 0;
+        console.log('taskId:', taskId, 'taskType:', taskType, 'taskStatus:', taskStatus);
+        const statusKey = this.getStatusKey(taskType, taskStatus);
+        console.log('statusKey:', statusKey);
+        const statusName = statusNames[statusKey] || `鐘舵��${taskStatus}`;
+        const editable = this.isEditable(taskType, taskStatus);
+        console.log('editable:', editable, '鏍规嵁 taskType:', taskType, 'taskStatus:', taskStatus);
+
+        // 闈炴柊寤虹姸鎬佺殑浠诲姟涓嶆樉绀哄湪寮圭獥涓�
+        if (!editable) {
+          console.log(`浠诲姟${taskId}鐘舵�乕${statusName}]涓嶆槸鏂板缓锛屽凡杩囨护`);
+          filteredCount++;
+          continue;
+        }
+
+        const item = {
+          taskId: taskId,
+          taskNum: row.taskNum ?? row.TaskNum ?? 0,
+          sourceAddress: row.sourceAddress || row.SourceAddress || '',
+          targetAddress: row.targetAddress || row.TargetAddress || '',
+          grade: row.grade ?? row.Grade ?? 1,
+          statusName: statusName,
+          palletCode: row.palletCode || row.PalletCode || '',
+          editable: editable,
+          taskType: taskType,
+          taskStatus: taskStatus
+        };
+        result.push(item);
+        // 淇濆瓨鍘熷鏁版嵁鐨勫壇鏈�
+        original.push({ ...item });
+      }
+      this.dispatchTableData = result;
+      this.dispatchOriginalData = original;
+      console.log('dispatchTableData result:', this.dispatchTableData);
+      console.log('dispatchOriginalData:', this.dispatchOriginalData);
+
+      // 濡傛灉鏈夎杩囨护鐨勪换鍔★紝鏄剧ず鎻愮ず
+      if (filteredCount > 0) {
+        this.$message.warning(`閫変腑鐨�${this.dispatchRows.length}涓换鍔′腑鏈�${filteredCount}涓潪鏂板缓鐘舵�佷换鍔″凡杩囨护`);
+      }
+    },
+    getStatusKey(taskType, taskStatus) {
+      if (taskType === 200) return `inbound_${taskStatus}`;
+      if (taskType === 100) return `outbound_${taskStatus}`;
+      if (taskType === 300) return `relocation_${taskStatus}`;
+      return `other_${taskStatus}`;
+    },
+    isEditable(taskType, taskStatus) {
+      if (taskType === 200 && taskStatus === 200) return true;
+      if (taskType === 100 && taskStatus === 100) return true;
+      if (taskType === 300 && taskStatus === 300) return true;
+      return false;
+    },
+    submitDispatch() {
+      if (this.dispatchTableData.length === 0) return this.$message.error("璇峰厛閫夋嫨浠诲姟");
+
+      const dispatchData = this.dispatchTableData.map(row => ({
+        taskId: row.taskId,
+        palletCode: row.palletCode,
+        sourceAddress: row.sourceAddress,
+        targetAddress: row.targetAddress,
+        grade: row.grade
+      }));
+      console.log('Dispatching data:', dispatchData);
+
+      this.dispatchLoading = true;
+      this.http
+        .post("/api/Task/DispatchTasksToWCS", dispatchData, "鏁版嵁澶勭悊涓�...")
+        .then((res) => {
+          this.dispatchLoading = false;
+          if (!res.status) {
+            this.$message.error(res.message);
+            if (res.data && res.data.failResults) {
+              this.dispatchFailResults = res.data.failResults;
+            }
+            return;
+          }
+
+          if (res.data && res.data.failCount === 0) {
+            this.$message.success(res.message);
+            this.showDispatch = false;
+            this.$emit("parentCall", ($vue) => { $vue.refresh(); });
+            return;
+          }
+
+          if (res.data && res.data.failResults) {
+            this.dispatchFailResults = res.data.failResults;
+          }
+
+          if (res.data && res.data.failCount > 0 && res.data.successCount > 0) {
+            this.$message.warning(res.message);
+          } else {
+            this.$message.error(res.message);
+          }
+        })
+        .catch(() => {
+          this.dispatchLoading = false;
+        });
+    }
+  }
+};
+</script>
+
+<style scoped>
+.dispatch-info {
+  font-size: 14px;
+  color: #606266;
+  margin-bottom: 10px;
+}
+.dispatch-info .count {
+  color: #409eff;
+  font-weight: bold;
+}
+.status-error {
+  color: #f56c6c;
+}
+.fail-results {
+  margin-top: 15px;
+  padding: 10px;
+  background: #fef0f0;
+  border-radius: 4px;
+}
+.fail-title {
+  font-size: 14px;
+  color: #f56c6c;
+  margin-bottom: 5px;
+}
+</style>
diff --git a/Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js b/Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js
index dde046b..be40abf 100644
--- a/Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js
+++ b/Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js
@@ -1,17 +1,17 @@
 
 //姝s鏂囦欢鏄敤鏉ヨ嚜瀹氫箟鎵╁睍涓氬姟浠g爜锛屽彲浠ユ墿灞曚竴浜涜嚜瀹氫箟椤甸潰鎴栬�呴噸鏂伴厤缃敓鎴愮殑浠g爜
-import addManualTask from './extend/addManualTask.vue'
+import gridBodyExtension from './extend/gridBodyExtension.vue'
 
 let extension = {
     components: {
       //鏌ヨ鐣岄潰鎵╁睍缁勪欢
       gridHeader: '',
-      gridBody: addManualTask,
+      gridBody: gridBodyExtension,
       gridFooter: '',
       //鏂板缓銆佺紪杈戝脊鍑烘鎵╁睍缁勪欢
       modelHeader: '',
       modelBody: '',
-      modelFooter: ''
+      modelFooter: '',
     },
     tableAction: '', //鎸囧畾鏌愬紶琛ㄧ殑鏉冮檺(杩欓噷濉啓琛ㄥ悕,榛樿涓嶇敤濉啓)
     buttons: { view: [], box: [], detail: [] }, //鎵╁睍鐨勬寜閽�
@@ -28,6 +28,18 @@
             this.$refs.gridBody.open();
           }
         });
+        //娣诲姞"鎵嬪姩涓嬪彂"鎸夐挳
+        this.buttons.push({
+          name: '鎵嬪姩涓嬪彂',
+          icon: 'el-icon-s-promotion',
+          type: 'primary',
+          value: 'DispatchTasksToWCS',
+          onClick: () => {
+            let rows = this.$refs.table.getSelected();
+            if (rows.length == 0) return this.$error("璇峰厛閫夋嫨浠诲姟");
+            this.$refs.gridBody.openDispatch(rows);
+          }
+        });
 
         let TaskHandCancelBtn = this.buttons.find(x => x.value == 'TaskHandCancel');
       if (TaskHandCancelBtn) {
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs
new file mode 100644
index 0000000..ad1a269
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs
@@ -0,0 +1,94 @@
+using System.Text.Json.Serialization;
+
+namespace WIDESEA_DTO.Task
+{
+    /// <summary>
+    /// 鎵嬪姩涓嬪彂浠诲姟Dto
+    /// </summary>
+    public class DispatchTaskDto
+    {
+        /// <summary>
+        /// 浠诲姟ID
+        /// </summary>
+        [JsonPropertyName("taskId")]
+        public long TaskId { get; set; }
+
+        /// <summary>
+        /// 鎵樼洏鍙�
+        /// </summary>
+        [JsonPropertyName("palletCode")]
+        public string PalletCode { get; set; }
+
+        /// <summary>
+        /// 璧风偣鍦板潃
+        /// </summary>
+        [JsonPropertyName("sourceAddress")]
+        public string SourceAddress { get; set; }
+
+        /// <summary>
+        /// 缁堢偣鍦板潃
+        /// </summary>
+        [JsonPropertyName("targetAddress")]
+        public string TargetAddress { get; set; }
+
+        /// <summary>
+        /// 浼樺厛绾�
+        /// </summary>
+        [JsonPropertyName("grade")]
+        public int Grade { get; set; }
+    }
+
+    /// <summary>
+    /// 浠诲姟涓嬪彂缁撴灉Dto
+    /// </summary>
+    public class DispatchTaskResultDto
+    {
+        /// <summary>
+        /// 浠诲姟ID
+        /// </summary>
+        [JsonPropertyName("taskId")]
+        public long TaskId { get; set; }
+
+        /// <summary>
+        /// 浠诲姟鍙�
+        /// </summary>
+        [JsonPropertyName("taskNum")]
+        public int TaskNum { get; set; }
+
+        /// <summary>
+        /// 鏄惁鎴愬姛
+        /// </summary>
+        [JsonPropertyName("success")]
+        public bool Success { get; set; }
+
+        /// <summary>
+        /// 閿欒淇℃伅
+        /// </summary>
+        [JsonPropertyName("errorMessage")]
+        public string ErrorMessage { get; set; }
+    }
+
+    /// <summary>
+    /// 鎵归噺涓嬪彂缁撴灉Dto
+    /// </summary>
+    public class DispatchResultDto
+    {
+        /// <summary>
+        /// 鎴愬姛鏁伴噺
+        /// </summary>
+        [JsonPropertyName("successCount")]
+        public int SuccessCount { get; set; }
+
+        /// <summary>
+        /// 澶辫触鏁伴噺
+        /// </summary>
+        [JsonPropertyName("failCount")]
+        public int FailCount { get; set; }
+
+        /// <summary>
+        /// 澶辫触浠诲姟鍒楄〃
+        /// </summary>
+        [JsonPropertyName("failResults")]
+        public List<DispatchTaskResultDto> FailResults { get; set; }
+    }
+}
\ No newline at end of file
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/ReceiveTaskResultDto.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/ReceiveTaskResultDto.cs
new file mode 100644
index 0000000..30cd3bd
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/ReceiveTaskResultDto.cs
@@ -0,0 +1,54 @@
+namespace WIDESEA_DTO.Task
+{
+    /// <summary>
+    /// WCS鎺ユ敹WMS浠诲姟鐨勭粨鏋淒to
+    /// </summary>
+    public class ReceiveTaskResultDto
+    {
+        /// <summary>
+        /// 鏄惁鎴愬姛
+        /// </summary>
+        public bool Success { get; set; }
+
+        /// <summary>
+        /// 娑堟伅
+        /// </summary>
+        public string Message { get; set; }
+
+        /// <summary>
+        /// 鏂板缓鐨勪换鍔℃暟閲�
+        /// </summary>
+        public int CreatedCount { get; set; }
+
+        /// <summary>
+        /// 閲嶅/宸插瓨鍦ㄧ殑浠诲姟鍒楄〃
+        /// </summary>
+        public List<DuplicateTaskDto> DuplicateTasks { get; set; }
+    }
+
+    /// <summary>
+    /// 閲嶅浠诲姟淇℃伅Dto
+    /// </summary>
+    public class DuplicateTaskDto
+    {
+        /// <summary>
+        /// 浠诲姟鍙�
+        /// </summary>
+        public int TaskNum { get; set; }
+
+        /// <summary>
+        /// 鎵樼洏鍙�
+        /// </summary>
+        public string PalletCode { get; set; }
+
+        /// <summary>
+        /// 浠诲姟绫诲瀷
+        /// </summary>
+        public int TaskType { get; set; }
+
+        /// <summary>
+        /// 褰撳墠鐘舵��
+        /// </summary>
+        public int TaskStatus { get; set; }
+    }
+}
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs
index e4954f6..bdf4510 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs
@@ -167,6 +167,13 @@
         /// <returns></returns>
         Task<WebResponseContent> CreateManualTaskAsync(CreateManualTaskDto dto);
 
+        /// <summary>
+        /// 鎵嬪姩涓嬪彂浠诲姟鍒癢CS
+        /// </summary>
+        /// <param name="dtos">涓嬪彂浠诲姟鍙傛暟鍒楄〃</param>
+        /// <returns>鎵归噺涓嬪彂缁撴灉</returns>
+        Task<WebResponseContent> DispatchTasksToWCSAsync(List<DispatchTaskDto> dtos);
+
 
         #region 鏋佸嵎搴撲换鍔℃ā鍧�
         /// <summary>
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs
deleted file mode 100644
index 3ff0a10..0000000
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs
+++ /dev/null
@@ -1,960 +0,0 @@
-using Mapster;
-using Microsoft.Extensions.Configuration;
-using SqlSugar;
-using WIDESEA_Common.Constants;
-using WIDESEA_Common.LocationEnum;
-using WIDESEA_Common.StockEnum;
-using WIDESEA_Common.TaskEnum;
-using WIDESEA_Common.WareHouseEnum;
-using WIDESEA_Core;
-using WIDESEA_Core.Helper;
-using WIDESEA_DTO.Stock;
-using WIDESEA_DTO.Task;
-using WIDESEA_Model.Models;
-
-namespace WIDESEA_TaskInfoService
-{
-    public partial class TaskService
-    {
-        #region WCS閫昏緫澶勭悊
-
-        /// <summary>
-        /// 鍒涘缓浠诲姟锛堢粍鐩樺叆搴撲换鍔°�佺┖鎵樼洏鍥炲簱浠诲姟锛�
-        /// </summary>
-        public async Task<WebResponseContent> CreateTaskInboundAsync(CreateTaskDto taskDto)
-        {
-            try
-            {
-                WebResponseContent content = await GetTaskByPalletCodeAsync(taskDto.PalletCode);
-                if (content.Status)
-                {
-                    return content;
-                }
-
-                if (string.IsNullOrWhiteSpace(taskDto.PalletCode) ||
-                    string.IsNullOrWhiteSpace(taskDto.Roadway))
-                {
-                    return WebResponseContent.Instance.Error("鏃犳晥鐨勪换鍔¤鎯�");
-                }
-
-                if (taskDto.TaskType != TaskTypeEnum.Inbound && taskDto.TaskType != TaskTypeEnum.InEmpty)
-                {
-                    return WebResponseContent.Instance.Error("鏃犳晥鐨勪换鍔¤鎯�");
-                }
-
-                // 浣跨敤 switch 琛ㄨ揪寮忔槧灏勪换鍔$被鍨�
-                int taskInboundType = taskDto.TaskType switch
-                {
-                    TaskTypeEnum.Inbound => TaskInboundTypeEnum.Inbound.GetHashCode(),
-                    TaskTypeEnum.InEmpty => TaskInboundTypeEnum.InEmpty.GetHashCode(),
-                    _ => 0 // 鐞嗚涓婁笉浼氳蛋鍒拌繖閲岋紝鍥犱负宸茬粡楠岃瘉杩囦簡
-                };
-
-                var task = new Dt_Task
-                {
-                    TaskNum = await BaseDal.GetTaskNo(),
-                    PalletCode = taskDto.PalletCode,
-                    PalletType = taskDto.PalletType,
-                    Roadway = taskDto.Roadway,
-                    TaskType = taskInboundType,
-                    TaskStatus = TaskInStatusEnum.InNew.GetHashCode(),
-                    SourceAddress = taskDto.SourceAddress,
-                    TargetAddress = taskDto.TargetAddress,
-                    CurrentAddress = taskDto.SourceAddress,
-                    NextAddress = taskDto.TargetAddress,
-                    WarehouseId = taskDto.WarehouseId,
-                    Grade = 1,
-                    Creater = "system"
-                };
-
-                var result = await Repository.AddDataAsync(task) > 0;
-                if (!result) return WebResponseContent.Instance.Error("浠诲姟鍒涘缓澶辫触");
-
-                var wmstaskDto = _mapper.Map<WMSTaskDTO>(task);
-                return WebResponseContent.Instance.OK("浠诲姟鍒涘缓鎴愬姛", wmstaskDto);
-            }
-            catch (Exception ex)
-            {
-                return WebResponseContent.Instance.Error($"浠诲姟鍒涘缓澶辫触: {ex.Message}");
-            }
-        }
-
-        /// <summary>
-        /// 鏍规嵁鎸囧畾鐨勪换鍔¤鎯呭紓姝ュ垱寤烘柊鐨勫嚭搴撲换鍔�
-        /// </summary>
-        public async Task<WebResponseContent> CreateTaskOutboundAsync(CreateTaskDto taskDto)
-        {
-            try
-            {
-                var stockResult = await _stockInfoService.GetStockInfoAsync(taskDto.WarehouseId);
-                if (stockResult == null || !stockResult.Any())
-                    return WebResponseContent.Instance.Error("鏈壘鍒板簱瀛樹俊鎭�");
-
-                var taskList = stockResult.Select(item => new Dt_Task
-                {
-                    WarehouseId = item.WarehouseId,
-                    PalletCode = item.PalletCode,
-                    PalletType = item.PalletType,
-                    SourceAddress = item.LocationCode,
-                    TargetAddress = taskDto.TargetAddress,
-                    Roadway = item.LocationDetails.RoadwayNo,
-                    TaskType = TaskTypeEnum.Outbound.GetHashCode(),
-                    TaskStatus = TaskStatusEnum.New.GetHashCode(),
-                    Grade = 1,
-                    TaskNum = 0,
-                    CurrentAddress = item.LocationCode,
-                    NextAddress = taskDto.TargetAddress,
-                    Creater = "system",
-                }).ToList();
-
-                var result = await BaseDal.AddDataAsync(taskList) > 0;
-                var wmstaskDto = result ? _mapper.Map<WMSTaskDTO>(taskList) : null;
-                return WebResponseContent.Instance.OK(result ? "浠诲姟鍒涘缓鎴愬姛" : "浠诲姟鍒涘缓澶辫触", wmstaskDto ?? new object());
-            }
-            catch (Exception ex)
-            {
-                return WebResponseContent.Instance.Error($"浠诲姟鍒涘缓澶辫触: {ex.Message}");
-            }
-        }
-
-        /// <summary>
-        /// 鑾峰彇鍙叆搴撹揣浣�
-        /// </summary>
-        public async Task<WebResponseContent> GetTasksLocationAsync(CreateTaskDto taskDto)
-        {
-            try
-            {
-                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
-                if (task == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑浠诲姟");
-
-                var locationInfo = await _locationInfoService.GetLocationInfo(task.Roadway);
-                if (locationInfo == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑璐т綅");
-
-                return await ExecuteWithinTransactionAsync(async () =>
-                {
-                    locationInfo.LocationStatus = LocationStatusEnum.FreeLock.GetHashCode();
-                    task.CurrentAddress = task.SourceAddress;
-                    task.NextAddress = locationInfo.LocationCode;
-                    task.TargetAddress = locationInfo.LocationCode;
-                    task.TaskStatus = TaskInStatusEnum.Line_InFinish.GetHashCode();
-
-                    var updateTaskResult = await BaseDal.UpdateDataAsync(task);
-                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(locationInfo);
-                    if (!updateTaskResult || !updateLocationResult)
-                    {
-                        return WebResponseContent.Instance.Error("浠诲姟鏇存柊澶辫触");
-                    }
-
-                    return WebResponseContent.Instance.OK("浠诲姟鏇存柊鎴愬姛", locationInfo.LocationCode);
-                });
-            }
-            catch (Exception ex)
-            {
-                return WebResponseContent.Instance.Error($"鑾峰彇浠诲姟澶辫触: {ex.Message}");
-            }
-        }
-
-        /// <summary>
-        /// 鍏ュ簱浠诲姟瀹屾垚锛氭坊鍔犲簱瀛橈紝淇敼璐т綅鐘舵�侊紝鍒犻櫎浠诲姟鏁版嵁锛屾坊鍔犲巻鍙蹭换鍔℃暟鎹�
-        /// </summary>
-        public async Task<WebResponseContent> InboundFinishTaskAsync(CreateTaskDto taskDto)
-        {
-            try
-            {
-                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
-                if (task == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑浠诲姟");
-
-                var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress);
-                if (location == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑璐т綅");
-
-                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
-                if (stockInfo == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴斿簱瀛樹俊鎭�");
-
-                // 鍒ゆ柇鏄笉鏄瀬鍗峰簱浠诲姟
-                if (taskDto.WarehouseId == (int)WarehouseEnum.FJ1 || taskDto.WarehouseId == (int)WarehouseEnum.ZJ1)
-                {
-                    return await CompleteAgvInboundTaskAsync(taskDto);
-                }
-
-                return await ExecuteWithinTransactionAsync(async () =>
-                {
-                    WebResponseContent content = new WebResponseContent();
-                    stockInfo.LocationCode = location.LocationCode;
-                    stockInfo.LocationId = location.Id;
-
-                    var now = DateTime.Now;
-                    if (task.Roadway.Contains("GW"))
-                    {
-                        if (stockInfo.Remark.IsNullOrEmpty())
-                        {
-                            stockInfo.OutboundDate = now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_GW1_FIRST);
-                            stockInfo.Remark = StockRemarkConstants.GW1;
-                        }
-                        else if (stockInfo.Remark == StockRemarkConstants.GW1)
-                        {
-                            stockInfo.OutboundDate = now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_GW1_SECOND);
-                            stockInfo.Remark = StockRemarkConstants.GW2;
-                        }
-                        else
-                        {
-                            stockInfo.OutboundDate = now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_GW1_FIRST);
-                        }
-                    }
-                    else if (task.Roadway.Contains("CW"))
-                    {
-                        if (stockInfo.Remark == StockRemarkConstants.GW2)
-                        {
-                            stockInfo.OutboundDate = now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_CW1);
-                            stockInfo.Remark = StockRemarkConstants.CW1;
-                        }
-                        else
-                        {
-                            stockInfo.OutboundDate = now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_CW1);
-                        }
-                    }
-                    else
-                    {
-                        stockInfo.OutboundDate = now;
-                    }
-
-                    stockInfo.StockStatus = StockStatusEmun.鍏ュ簱瀹屾垚.GetHashCode();
-
-                    location.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
-
-                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
-                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
-                    if (!updateLocationResult || !updateStockResult)
-                        return WebResponseContent.Instance.Error("浠诲姟瀹屾垚澶辫触");
-                    // 璋冪敤MES鎵樼洏杩涚珯
-                    //var inboundRequest = new InboundInContainerRequest
-                    //{
-                    //    EquipmentCode = "STK-GROUP-001",
-                    //    ResourceCode = "STK-GROUP-001",
-                    //    LocalTime = DateTime.Now,
-                    //    ContainerCode = taskDto.PalletCode
-                    //};
-                    //var inboundResult = _mesService.InboundInContainer(inboundRequest);
-                    //if (inboundResult == null || inboundResult.Data == null || !inboundResult.Data.IsSuccess)
-                    //{
-                    //    return content.Error($"浠诲姟瀹屾垚澶辫触锛歁ES杩涚珯澶辫触: {inboundResult?.Data?.Msg ?? inboundResult?.ErrorMessage ?? "鏈煡閿欒"}");
-                    //}
-                    return await CompleteTaskAsync(task, "鍏ュ簱瀹屾垚");
-                });
-            }
-            catch (Exception ex)
-            {
-                return WebResponseContent.Instance.Error($"瀹屾垚浠诲姟澶辫触: {ex.Message}");
-            }
-        }
-
-        /// <summary>
-        /// 鍑哄簱浠诲姟瀹屾垚 锛氫慨鏀瑰簱瀛橈紝淇敼璐т綅鐘舵�侊紝鍒犻櫎浠诲姟鏁版嵁锛屾坊鍔犲巻鍙蹭换鍔℃暟鎹�
-        /// </summary>
-        public async Task<WebResponseContent> OutboundFinishTaskAsync(CreateTaskDto taskDto)
-        {
-            try
-            {
-                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
-                if (task == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑浠诲姟");
-
-                var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress);
-                if (location == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑璐т綅");
-
-                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
-                if (stockInfo == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴斿簱瀛樹俊鎭�");
-
-                // 鍒ゆ柇鏄笉鏄瀬鍗峰簱浠诲姟
-                if (taskDto.WarehouseId == (int)WarehouseEnum.FJ1 || taskDto.WarehouseId == (int)WarehouseEnum.ZJ1)
-                {
-                    OutTaskCompleteDto outTaskCompleteDto = new OutTaskCompleteDto()
-                    {
-                        TaskId = task.OrderNo ?? string.Empty,
-                        DevId = task.TargetAddress ?? string.Empty,
-                        ReqTime = DateTime.Now.ToString()
-                    };
-                    return await OutTaskComplete(outTaskCompleteDto);
-                }
-
-                WebResponseContent content = new WebResponseContent();
-                return await ExecuteWithinTransactionAsync(async () =>
-                {
-                    stockInfo.LocationId = 0;
-                    stockInfo.LocationCode = string.Empty;
-                    stockInfo.OutboundDate = DateTime.Now;
-
-                    location.LocationStatus = LocationStatusEnum.Free.GetHashCode();
-
-                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
-                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
-                    if (!updateLocationResult || !updateStockResult)
-                        return WebResponseContent.Instance.Error("浠诲姟瀹屾垚澶辫触");
-
-                    // 楂樻俯2鍙峰嚭搴撳埌CWSC1鏃讹紝鑷姩鍒涘缓鍏ュ簱浠诲姟鍒板父娓�1鍙峰贩閬�
-                    WMSTaskDTO? inboundTaskDto = null;
-                    if (task.TargetAddress == TaskAddressConstants.GW2_ADDRESS)
-                    {
-                        var inboundTask = new Dt_Task
-                        {
-                            TaskNum = await BaseDal.GetTaskNo(),
-                            PalletCode = task.PalletCode,
-                            PalletType = task.PalletType,
-                            Roadway = "CW1",
-                            TaskType = TaskInboundTypeEnum.Inbound.GetHashCode(),
-                            TaskStatus = TaskInStatusEnum.InNew.GetHashCode(),
-                            SourceAddress = task.TargetAddress,
-                            TargetAddress = task.TargetAddress,
-                            CurrentAddress = task.TargetAddress,
-                            NextAddress = task.TargetAddress,
-                            WarehouseId = (int)WarehouseEnum.CW1,
-                            Grade = 1,
-                            Creater = "system_auto"
-                        };
-                        await Repository.AddDataAsync(inboundTask);
-                        inboundTaskDto = _mapper.Map<WMSTaskDTO>(inboundTask);
-                    }
-
-                    var completeResult = await CompleteTaskAsync(task, "鍑哄簱瀹屾垚");
-                    if (!completeResult.Status)
-                        return completeResult;
-
-                    // 杩斿洖鍏ュ簱浠诲姟淇℃伅锛堝鏋滄湁锛�
-                    if (inboundTaskDto != null)
-                    {
-                        return content.OK("鍑哄簱瀹屾垚锛屽凡鍒涘缓鍏ュ簱浠诲姟", inboundTaskDto);
-                    }
-                    return content.OK("鍑哄簱瀹屾垚");
-                });
-            }
-            catch (Exception ex)
-            {
-                return WebResponseContent.Instance.Error($"瀹屾垚浠诲姟澶辫触: {ex.Message}");
-            }
-        }
-
-        /// <summary>
-        /// 绉诲簱浠诲姟瀹屾垚锛氫慨鏀瑰簱瀛樹綅缃笌鐘舵�侊紝淇敼婧�/鐩爣璐т綅鐘舵�侊紝鍒犻櫎浠诲姟鏁版嵁
-        /// </summary>
-        public async Task<WebResponseContent> RelocationFinishTaskAsync(CreateTaskDto taskDto)
-        {
-            try
-            {
-                var task = await BaseDal.QueryFirstAsync(s =>
-                    s.PalletCode == taskDto.PalletCode &&
-                    s.TaskType == TaskRelocationTypeEnum.Relocation.GetHashCode());
-                if (task == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑绉诲簱浠诲姟");
-
-                var sourceLocation = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress);
-                if (sourceLocation == null) return WebResponseContent.Instance.Error("鏈壘鍒扮Щ搴撴簮璐т綅");
-
-                var targetLocation = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress);
-                if (targetLocation == null) return WebResponseContent.Instance.Error("鏈壘鍒扮Щ搴撶洰鏍囪揣浣�");
-
-                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
-                if (stockInfo == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴斿簱瀛樹俊鎭�");
-
-                return await ExecuteWithinTransactionAsync(async () =>
-                {
-                    stockInfo.LocationCode = targetLocation.LocationCode;
-                    stockInfo.LocationId = targetLocation.Id;
-                    stockInfo.StockStatus = StockStatusEmun.鍏ュ簱瀹屾垚.GetHashCode();
-
-                    sourceLocation.LocationStatus = LocationStatusEnum.Free.GetHashCode();
-                    targetLocation.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
-
-                    var updateSourceResult = await _locationInfoService.UpdateLocationInfoAsync(sourceLocation);
-                    var updateTargetResult = await _locationInfoService.UpdateLocationInfoAsync(targetLocation);
-                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
-
-                    if (!updateSourceResult || !updateTargetResult || !updateStockResult)
-                        return WebResponseContent.Instance.Error("绉诲簱浠诲姟瀹屾垚澶辫触");
-
-                    return await CompleteTaskAsync(task, "绉诲簱瀹屾垚");
-                });
-            }
-            catch (Exception ex)
-            {
-                return WebResponseContent.Instance.Error($"瀹屾垚浠诲姟澶辫触: {ex.Message}");
-            }
-        }
-
-        /// <summary>
-        /// 鍒涘缓绌烘墭鐩樺叆搴撲换鍔�
-        /// </summary>
-        /// <param name="taskDto"></param>
-        /// <returns></returns>
-        public async Task<WebResponseContent> CreateTaskInboundTrayAsync(CreateTaskDto taskDto)
-        {
-            try
-            {
-                WebResponseContent content = await GetTaskByPalletCodeAsync(taskDto.PalletCode);
-                if (content.Status)
-                {
-                    return content;
-                }
-
-                //var tasks = await BaseDal.QueryAsync(s => s.PalletCode == palletCode);
-                //if (tasks == null || !tasks.Any())
-                //    return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑浠诲姟");
-                //var taskDtos = _mapper.Map<List<WMSTaskDTO>>(tasks);
-                return WebResponseContent.Instance.OK("鏌ヨ鎴愬姛"/*, taskDtos*/);
-            }
-            catch (Exception ex)
-            {
-                return WebResponseContent.Instance.Error($"鏌ヨ浠诲姟澶辫触: {ex.Message}");
-            }
-        }
-
-        /// <summary>
-        /// 绌烘墭鐩樺叆搴撳畬鎴�
-        /// </summary>
-        public async Task<WebResponseContent> InboundFinishTaskTrayAsync(CreateTaskDto taskDto)
-        {
-            try
-            {
-                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
-                if (task == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑浠诲姟");
-
-                var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress);
-                if (location == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑璐т綅");
-
-                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
-                if (stockInfo == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴斿簱瀛樹俊鎭�");
-
-                return await ExecuteWithinTransactionAsync(async () =>
-                {
-                    stockInfo.LocationCode = location.LocationCode;
-                    stockInfo.LocationId = location.Id;
-                    stockInfo.StockStatus = StockStatusEmun.绌烘墭鐩樺簱瀛�.GetHashCode();
-
-                    location.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
-
-                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
-                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
-                    if (!updateLocationResult || !updateStockResult)
-                        return WebResponseContent.Instance.Error("浠诲姟瀹屾垚澶辫触");
-
-                    var saveTaskHistoryResult = await SaveTaskHistoryAsync(task, "绌烘墭鐩樺叆搴撳畬鎴�");
-                    if (!saveTaskHistoryResult.Status)
-                        return saveTaskHistoryResult;
-
-                    var saveStockHistoryResult = await SaveStockHistoryAsync(stockInfo, "绌烘墭鐩樺叆搴撳畬鎴�");
-                    if (!saveStockHistoryResult.Status)
-                        return saveStockHistoryResult;
-
-                    var deleteResult = await BaseDal.DeleteDataAsync(task);
-                    if (!deleteResult) return WebResponseContent.Instance.Error("浠诲姟瀹屾垚澶辫触");
-
-                    return WebResponseContent.Instance.OK("浠诲姟瀹屾垚");
-                });
-            }
-            catch (Exception ex)
-            {
-                return WebResponseContent.Instance.Error($"瀹屾垚浠诲姟澶辫触: {ex.Message}");
-            }
-        }
-
-        /// <summary>
-        /// 鍒涘缓绌烘墭鐩樺嚭搴撲换鍔�
-        /// </summary>
-        /// <param name="taskDto"></param>
-        /// <returns></returns>
-        public async Task<WebResponseContent> GetOutBoundTrayTaskAsync(CreateTaskDto taskDto)
-        {
-            try
-            {
-                var dt_Task = await BaseDal.QueryFirstAsync(x => x.TargetAddress == taskDto.TargetAddress);
-                if (!dt_Task.IsNullOrEmpty())
-                {
-                    var taskDTO = dt_Task.Adapt<WMSTaskDTO>();
-                    return WebResponseContent.Instance.OK("浠诲姟鍒涘缓鎴愬姛", taskDTO);
-                }
-
-                var stockInfo = await _stockInfoService.Repository.QueryDataNavFirstAsync(x => x.LocationDetails.WarehouseId == taskDto.WarehouseId && x.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode() && x.StockStatus == StockStatusEmun.绌烘墭鐩樺簱瀛�.GetHashCode());
-                if (stockInfo == null)
-                    return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑搴撳瓨淇℃伅");
-
-                var task = new Dt_Task()
-                {
-                    WarehouseId = stockInfo.LocationDetails.WarehouseId,
-                    PalletCode = stockInfo.PalletCode,
-                    PalletType = stockInfo.PalletType,
-                    SourceAddress = stockInfo.LocationCode,
-                    CurrentAddress = stockInfo.LocationCode,
-                    NextAddress = taskDto.TargetAddress,
-                    TargetAddress = taskDto.TargetAddress,
-                    Roadway = stockInfo.LocationDetails.RoadwayNo,
-                    TaskType = TaskOutboundTypeEnum.OutEmpty.GetHashCode(),
-                    TaskStatus = TaskStatusEnum.New.GetHashCode(),
-                    Grade = 1,
-                    TaskNum = await BaseDal.GetTaskNo(),
-                    Creater = "system",
-                };
-
-                return await ExecuteWithinTransactionAsync(async () =>
-                {
-                    var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationId);
-                    locationInfo.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode();
-                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(locationInfo);
-                    if (!updateLocationResult)
-                        return WebResponseContent.Instance.Error("浠诲姟鍒涘缓澶辫触");
-
-                    var taskDtos = task.Adapt<WMSTaskDTO>();
-
-                    var addResult = await BaseDal.AddDataAsync(task) > 0;
-                    if (!addResult)
-                        return WebResponseContent.Instance.Error("浠诲姟鍒涘缓澶辫触");
-                    return WebResponseContent.Instance.OK("浠诲姟鍒涘缓鎴愬姛", taskDtos);
-                });
-            }
-            catch (Exception ex)
-            {
-                return WebResponseContent.Instance.Error($"鏌ヨ浠诲姟澶辫触: {ex.Message}");
-            }
-        }
-
-        /// <summary>
-        /// 绌烘墭鐩樺嚭搴撳畬鎴�
-        /// </summary>
-        public async Task<WebResponseContent> OutboundFinishTaskTrayAsync(CreateTaskDto taskDto)
-        {
-            try
-            {
-                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
-                if (task == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑浠诲姟");
-
-                var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress);
-                if (location == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑璐т綅");
-
-                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
-                if (stockInfo == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴斿簱瀛樹俊鎭�");
-
-                return await ExecuteWithinTransactionAsync(async () =>
-                {
-                    stockInfo.LocationId = 0;
-                    stockInfo.LocationCode = string.Empty;
-                    stockInfo.StockStatus = StockStatusEmun.鍑哄簱瀹屾垚.GetHashCode();
-
-                    location.LocationStatus = LocationStatusEnum.Free.GetHashCode();
-
-                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
-                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
-                    if (!updateLocationResult || !updateStockResult)
-                        return WebResponseContent.Instance.Error("浠诲姟瀹屾垚澶辫触");
-
-                    var saveTaskHistoryResult = await SaveTaskHistoryAsync(task, "绌烘墭鐩樺嚭搴撳畬鎴�");
-                    if (!saveTaskHistoryResult.Status)
-                        return saveTaskHistoryResult;
-
-                    var saveStockHistoryResult = await SaveStockHistoryAsync(stockInfo, "绌烘墭鐩樺嚭搴撳畬鎴�");
-                    if (!saveStockHistoryResult.Status)
-                        return saveStockHistoryResult;
-
-                    var deleteResult = await BaseDal.DeleteDataAsync(task);
-                    if (!deleteResult) return WebResponseContent.Instance.Error("浠诲姟瀹屾垚澶辫触");
-
-                    return WebResponseContent.Instance.OK("浠诲姟瀹屾垚");
-                });
-            }
-            catch (Exception ex)
-            {
-                return WebResponseContent.Instance.Error($"瀹屾垚浠诲姟澶辫触: {ex.Message}");
-            }
-        }
-
-        /// <summary>
-        /// 淇敼浠诲姟鐘舵�侊紙鏍规嵁浠诲姟ID淇敼涓烘寚瀹氱姸鎬侊級
-        /// </summary>
-        /// <param name="taskId"></param>
-        /// <param name="newStatus"></param>
-        /// <returns></returns>
-        public async Task<WebResponseContent> UpdateTaskByStatusAsync(UpdateTaskDto taskDto)
-        {
-            try
-            {
-                var taskInfo = await BaseDal.QueryFirstAsync(s => s.TaskNum == taskDto.Id);
-                if (taskInfo == null)
-                    return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑浠诲姟");
-
-                taskInfo.TaskStatus = taskDto.NewStatus;
-                taskInfo.NextAddress = taskDto.NextAddress;
-                taskInfo.CurrentAddress = taskDto.CurrentAddress;
-                await BaseDal.UpdateDataAsync(taskInfo);
-
-                return WebResponseContent.Instance.OK("淇敼鎴愬姛", taskInfo);
-            }
-            catch (Exception ex)
-            {
-                return WebResponseContent.Instance.Error($"淇敼澶辫触: {ex.Message}");
-            }
-        }
-
-        /// <summary>
-        /// 鑷姩鍒涘缓鍑哄簱浠诲姟 - 鏌ヨ鍒版湡搴撳瓨骞跺垱寤轰换鍔�
-        /// </summary>
-        public async Task<WebResponseContent> CreateAutoOutboundTasksAsync()
-        {
-            try
-            {
-                // 1. 鏌ヨ鍒版湡搴撳瓨
-                var expiredStocks = await _stockInfoService.Repository
-                    .QueryDataNavAsync(s => s.OutboundDate <= DateTime.Now
-                        && s.StockStatus == StockStatusEmun.鍏ュ簱瀹屾垚.GetHashCode());
-
-                if (expiredStocks == null || !expiredStocks.Any())
-                {
-                    return WebResponseContent.Instance.OK("鏃犲埌鏈熷簱瀛橀渶瑕佸鐞�");
-                }
-
-                // 杩囨护鏈変綅缃笖浣嶇疆鏈夊簱瀛樼殑璁板綍
-                expiredStocks = expiredStocks
-                    .Where(s => s.LocationDetails != null
-                        && s.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode())
-                    .ToList();
-
-                if (!expiredStocks.Any())
-                {
-                    return WebResponseContent.Instance.OK("鏃犵鍚堟潯浠剁殑鍒版湡搴撳瓨");
-                }
-
-                // 2. 妫�鏌ュ凡瀛樺湪鐨勪换鍔�
-                var palletCodes = expiredStocks.Select(s => s.PalletCode).ToList();
-                var existingTasks = await Repository.QueryDataAsync(t =>
-                    palletCodes.Contains(t.PalletCode)
-                    && (t.TaskStatus == TaskStatusEnum.New.GetHashCode()
-                        || t.TaskStatus == TaskStatusEnum.SC_Executing.GetHashCode()
-                        || t.TaskStatus == TaskInStatusEnum.InNew.GetHashCode()));
-
-                var processedPallets = existingTasks.Select(t => t.PalletCode).ToHashSet();
-
-                // 3. 绛涢�夐渶瑕佸鐞嗙殑搴撳瓨
-                var stocksToProcess = expiredStocks
-                    .Where(s => !processedPallets.Contains(s.PalletCode))
-                    .ToList();
-
-                if (!stocksToProcess.Any())
-                {
-                    return WebResponseContent.Instance.OK("鎵�鏈夊埌鏈熷簱瀛樺凡瀛樺湪浠诲姟");
-                }
-
-                // 4. 鑾峰彇閰嶇疆鐨勭洰鏍囧湴鍧�鏄犲皠
-                var targetAddressMap = _configuration.GetSection("AutoOutboundTask:TargetAddresses")
-                    .Get<Dictionary<string, List<string>>>()
-                    ?? new Dictionary<string, List<string>>();
-
-                // 5. 鎵归噺鍒涘缓浠诲姟
-                var taskList = new List<Dt_Task>();
-                foreach (var stock in stocksToProcess)
-                {
-                    // 鏍规嵁宸烽亾纭畾鐩爣鍦板潃锛堜紭鍏堟牴鎹� Remark 纭畾锛孯emark 涓虹┖鍒欐牴鎹贩閬撻厤缃級
-                    var targetAddress = DetermineTargetAddressByRemark(
-                        stock.Remark ?? "",
-                        stock.LocationDetails?.RoadwayNo ?? "",
-                        targetAddressMap);
-
-                    var task = new Dt_Task
-                    {
-                        WarehouseId = stock.WarehouseId,
-                        PalletCode = stock.PalletCode,
-                        PalletType = stock.PalletType,
-                        SourceAddress = stock.LocationCode,
-                        CurrentAddress = stock.LocationCode,
-                        NextAddress = targetAddress,
-                        TargetAddress = targetAddress,
-                        Roadway = stock.LocationDetails?.RoadwayNo ?? "",
-                        TaskType = TaskTypeEnum.Outbound.GetHashCode(),
-                        TaskStatus = TaskStatusEnum.New.GetHashCode(),
-                        Grade = 1,
-                        TaskNum = await BaseDal.GetTaskNo(),
-                        Creater = "system_auto"
-                    };
-                    taskList.Add(task);
-                }
-
-                var transactionResult = await ExecuteWithinTransactionAsync(async () =>
-                {
-                    var addResult = await BaseDal.AddDataAsync(taskList) > 0;
-                    if (!addResult)
-                    {
-                        return WebResponseContent.Instance.Error($"鎵归噺鍒涘缓浠诲姟澶辫触锛屽叡 {taskList.Count} 涓换鍔�");
-                    }
-
-                    // 浠诲姟鍒涘缓鎴愬姛鍚庯紝鍚屾閿佸畾搴撳瓨鍜岃揣浣嶇姸鎬侊紝閬垮厤閲嶅鍒嗛厤
-                    var stocksToUpdate = stocksToProcess
-                        .Select(s =>
-                        {
-                            s.StockStatus = StockStatusEmun.鍑哄簱閿佸畾.GetHashCode();
-                            return s;
-                        })
-                        .ToList();
-
-                    var updateStockResult = await _stockInfoService.Repository.UpdateDataAsync(stocksToUpdate);
-                    if (!updateStockResult)
-                    {
-                        return WebResponseContent.Instance.Error($"浠诲姟鍒涘缓鎴愬姛锛屼絾搴撳瓨鐘舵�佹洿鏂板け璐ワ紝鍏� {stocksToUpdate.Count} 鏉�");
-                    }
-
-                    var locationsToUpdate = stocksToProcess
-                        .Where(s => s.LocationDetails != null)
-                        .GroupBy(s => s.LocationDetails.Id)
-                        .Select(g =>
-                        {
-                            var location = g.First().LocationDetails;
-                            location.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode();
-                            return location;
-                        })
-                        .ToList();
-
-                    if (locationsToUpdate.Any())
-                    {
-                        var updateLocationResult = await _locationInfoService.Repository.UpdateDataAsync(locationsToUpdate);
-                        if (!updateLocationResult)
-                        {
-                            return WebResponseContent.Instance.Error($"浠诲姟鍒涘缓鎴愬姛锛屼絾璐т綅鐘舵�佹洿鏂板け璐ワ紝鍏� {locationsToUpdate.Count} 鏉�");
-                        }
-                    }
-
-                    return WebResponseContent.Instance.OK();
-                });
-                if (!transactionResult.Status)
-                {
-                    return transactionResult;
-                }
-
-                // 6. 閫氱煡 WCS锛堝紓姝ワ紝涓嶅奖鍝嶄富娴佺▼锛�
-                _ = Task.Run(() =>
-                {
-                    try
-                    {
-                        var wmstaskDtos = _mapper.Map<List<WMSTaskDTO>>(taskList);
-                        _httpClientHelper.Post<WebResponseContent>(
-                            "http://localhost:9292/api/Task/ReceiveTask",
-                            wmstaskDtos.ToJson());
-                    }
-                    catch (Exception ex)
-                    {
-                        // WCS 閫氱煡澶辫触涓嶅奖鍝嶄换鍔″垱寤猴紝璁板綍鏃ュ織鍗冲彲
-                        Console.WriteLine($"WCS 鎵归噺閫氱煡澶辫触锛屼换鍔℃暟閲�: {taskList.Count}, 閿欒: {ex.Message}");
-                    }
-                });
-
-                return WebResponseContent.Instance.OK($"鎴愬姛鍒涘缓 {taskList.Count} 涓嚭搴撲换鍔�", taskList.Count);
-            }
-            catch (Exception ex)
-            {
-                return WebResponseContent.Instance.Error($"鑷姩鍒涘缓鍑哄簱浠诲姟澶辫触: {ex.Message}");
-            }
-        }
-
-        /// <summary>
-        /// 鍒涘缓鏈烘鎵嬬粍鐩樹换鍔�
-        /// </summary>
-        public async Task<WebResponseContent> CreateRobotGroupPalletTaskAsync(StockDTO stock)
-        {
-            return await CreateRobotPalletTaskAsync(
-                stock,
-                "缁勭洏",
-                RobotTaskTypeEnum.GroupPallet,
-                s => string.IsNullOrWhiteSpace(s.TargetPalletNo) ? s.SourcePalletNo : s.TargetPalletNo,
-                requireStockWithoutLocation: false);
-        }
-
-        /// <summary>
-        /// 鍒涘缓鏈烘鎵嬫崲鐩樹换鍔�
-        /// </summary>
-        public async Task<WebResponseContent> CreateRobotChangePalletTaskAsync(StockDTO stock)
-        {
-            return await CreateRobotPalletTaskAsync(
-                stock,
-                "鎹㈢洏",
-                RobotTaskTypeEnum.ChangePallet,
-                s => s.SourcePalletNo,
-                requireStockWithoutLocation: true,
-                stockPalletCodeSelector: s => s.SourcePalletNo);
-        }
-
-        /// <summary>
-        /// 鍒涘缓鏈烘鎵嬫媶鐩樹换鍔�
-        /// </summary>
-        public async Task<WebResponseContent> CreateRobotSplitPalletTaskAsync(StockDTO stock)
-        {
-            return await CreateRobotPalletTaskAsync(
-                stock,
-                "鎷嗙洏",
-                RobotTaskTypeEnum.SplitPallet,
-                s => s.SourcePalletNo,
-                requireStockWithoutLocation: true,
-                stockPalletCodeSelector: s => s.SourcePalletNo);
-        }
-
-        private async Task<WebResponseContent> CreateRobotPalletTaskAsync(
-            StockDTO stock,
-            string taskName,
-            RobotTaskTypeEnum taskType,
-            Func<StockDTO, string?> palletCodeSelector,
-            bool requireStockWithoutLocation,
-            Func<StockDTO, string?>? stockPalletCodeSelector = null)
-        {
-            try
-            {
-                if (stock == null)
-                    return WebResponseContent.Instance.Error("浠诲姟鍙傛暟涓嶈兘涓虹┖");
-
-                var palletCode = palletCodeSelector(stock)?.Trim();
-                if (string.IsNullOrWhiteSpace(palletCode))
-                    return WebResponseContent.Instance.Error("鎵樼洏鍙蜂笉鑳戒负绌�");
-
-                var sourceLineNo = stock.SourceLineNo?.Trim();
-                var targetLineNo = stock.TargetLineNo?.Trim();
-                if (string.IsNullOrWhiteSpace(sourceLineNo) || string.IsNullOrWhiteSpace(targetLineNo))
-                    return WebResponseContent.Instance.Error("鏉ユ簮绾夸綋缂栧彿鍜岀洰鏍囩嚎浣撶紪鍙蜂笉鑳戒负绌�");
-
-                var existingTask = await BaseDal.QueryFirstAsync(t =>
-                    t.PalletCode == palletCode &&
-                    (t.TaskStatus == TaskRobotStatusEnum.RobotNew.GetHashCode()
-                     || t.TaskStatus == TaskRobotStatusEnum.RobotExecuting.GetHashCode()
-                     || t.TaskStatus == TaskRobotStatusEnum.RobotPickFinish.GetHashCode()
-                     || t.TaskStatus == TaskRobotStatusEnum.RobotPutFinish.GetHashCode()
-                     || t.TaskStatus == TaskRobotStatusEnum.RobotPending.GetHashCode()));
-                if (existingTask != null)
-                    return WebResponseContent.Instance.Error($"鎵樼洏[{palletCode}]宸插瓨鍦ㄦ湭瀹屾垚浠诲姟");
-
-                Dt_StockInfo? stockInfo = null;
-                if (requireStockWithoutLocation)
-                {
-                    var stockPalletCode = (stockPalletCodeSelector ?? palletCodeSelector).Invoke(stock)?.Trim();
-                    if (string.IsNullOrWhiteSpace(stockPalletCode))
-                        return WebResponseContent.Instance.Error("婧愭墭鐩樺彿涓嶈兘涓虹┖");
-
-                    stockInfo = await _stockInfoService.GetStockInfoAsync(stockPalletCode);
-                    if (stockInfo == null)
-                        return WebResponseContent.Instance.Error($"鎵樼洏[{stockPalletCode}]搴撳瓨涓嶅瓨鍦�");
-
-                    if (stockInfo.LocationId > 0 || !string.IsNullOrWhiteSpace(stockInfo.LocationCode))
-                        return WebResponseContent.Instance.Error($"鎵樼洏[{stockPalletCode}]搴撳瓨宸茬粦瀹氳揣浣嶏紝涓嶈兘鍒涘缓鏈烘鎵媨taskName}浠诲姟");
-                }
-                var section = App.Configuration.GetSection("RobotTaskAddressRules").GetSection(targetLineNo).GetChildren().Select(c => c.Value).ToArray();
-
-                var task = new Dt_Task
-                {
-                    TaskNum = await BaseDal.GetTaskNo(),
-                    PalletCode = palletCode,
-                    PalletType = stockInfo?.PalletType ?? 0,
-                    Roadway = stock.Roadway ?? string.Empty,
-                    TaskType = taskType.GetHashCode(),
-                    TaskStatus = TaskRobotStatusEnum.RobotNew.GetHashCode(),
-                    SourceAddress = section[0],
-                    TargetAddress = section[1],
-                    CurrentAddress = targetLineNo,
-                    NextAddress = targetLineNo,
-                    WarehouseId = stockInfo?.WarehouseId ?? 1,
-                    Grade = 1,
-                    Remark = $"鏈烘鎵媨taskName}",
-                    Creater = "system"
-                };
-
-                var result = await Repository.AddDataAsync(task) > 0;
-                if (!result)
-                    return WebResponseContent.Instance.Error($"鏈烘鎵媨taskName}浠诲姟鍒涘缓澶辫触");
-
-                var wmstaskDto = _mapper.Map<WMSTaskDTO>(task) ?? new WMSTaskDTO();
-                return WebResponseContent.Instance.OK($"鏈烘鎵媨taskName}浠诲姟鍒涘缓鎴愬姛", wmstaskDto);
-            }
-            catch (Exception ex)
-            {
-                return WebResponseContent.Instance.Error($"鏈烘鎵媨taskName}浠诲姟鍒涘缓澶辫触: {ex.Message}");
-            }
-        }
-
-        /// <summary>
-        /// 鎵嬪姩鍒涘缓浠诲姟
-        /// </summary>
-        /// <param name="dto">鎵嬪姩鍒涘缓浠诲姟鍙傛暟</param>
-        /// <returns></returns>
-        public async Task<WebResponseContent> CreateManualTaskAsync(CreateManualTaskDto dto)
-        {
-            try
-            {
-                int taskType;
-                int taskStatus;
-                switch (dto.TaskType)
-                {
-                    case "鍏ュ簱":
-                        taskType = TaskInboundTypeEnum.Inbound.GetHashCode();
-                        taskStatus = TaskInStatusEnum.InNew.GetHashCode();
-                        break;
-
-                    case "鍑哄簱":
-                        taskType = TaskOutboundTypeEnum.Outbound.GetHashCode();
-                        taskStatus = TaskOutStatusEnum.OutNew.GetHashCode();
-                        break;
-
-                    case "绉诲簱":
-                        taskType = TaskRelocationTypeEnum.Relocation.GetHashCode();
-                        taskStatus = TaskRelocationStatusEnum.RelocationNew.GetHashCode();
-                        break;
-
-                    default:
-                        return WebResponseContent.Instance.Error($"涓嶆敮鎸佺殑浠诲姟绫诲瀷: {dto.TaskType}");
-                }
-
-                int taskNum = await BaseDal.GetTaskNo();
-
-                var task = new Dt_Task
-                {
-                    TaskNum = taskNum,
-                    PalletCode = dto.Barcode,
-                    SourceAddress = dto.SourceAddress,
-                    TargetAddress = dto.TargetAddress,
-                    TaskType = taskType,
-                    TaskStatus = taskStatus,
-                    Grade = dto.Grade,
-                    Roadway = dto.TargetAddress,
-                    WarehouseId = dto.WarehouseId,
-                    CurrentAddress = dto.SourceAddress,
-                    NextAddress = dto.TargetAddress,
-                    Creater = "manual",
-                    CreateDate = DateTime.Now,
-                    ModifyDate = DateTime.Now
-                };
-
-                var wmsTaskDtos = new List<WMSTaskDTO>()
-                {
-                    new()
-                    {
-                        TaskNum = task.TaskNum,
-                        PalletCode = task.PalletCode,
-                        SourceAddress = task.SourceAddress,
-                        TargetAddress = task.TargetAddress,
-                        TaskType = task.TaskType,
-                        Roadway = task.Roadway,
-                        TaskStatus = task.TaskStatus,
-                        WarehouseId = task.WarehouseId
-                    }
-                };
-
-               return await _unitOfWorkManage.BeginTranAsync(async () =>
-                {
-                    // 4. 淇濆瓨鍒版暟鎹簱
-                    var result = await BaseDal.AddDataAsync(task) > 0;
-                    if (!result)
-                        return WebResponseContent.Instance.Error("鍒涘缓浠诲姟澶辫触");
-
-                    var wcsResult = _httpClientHelper.Post<WebResponseContent>(
-                        "http://localhost:9292/api/Task/ReceiveManualTask",
-                        wmsTaskDtos.ToJson());
-
-                    if (!wcsResult.IsSuccess || !wcsResult.Data.Status)
-                        return WebResponseContent.Instance.Error($"浠诲姟宸插垱寤轰絾鍙戦�佺粰WCS澶辫触: {wcsResult.Data?.Message}");
-
-                    return WebResponseContent.Instance.OK($"鎵嬪姩鍒涘缓浠诲姟鎴愬姛锛屼换鍔″彿: {taskNum}");
-                });
-            }
-            catch (Exception ex)
-            {
-                return WebResponseContent.Instance.Error($"鎵嬪姩鍒涘缓浠诲姟寮傚父: {ex.Message}");
-            }
-        }
-
-        #endregion WCS閫昏緫澶勭悊
-    }
-}
\ No newline at end of file
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_AutoOutbound.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_AutoOutbound.cs
new file mode 100644
index 0000000..bf5137d
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_AutoOutbound.cs
@@ -0,0 +1,175 @@
+using Microsoft.Extensions.Configuration;
+using WIDESEA_Common.LocationEnum;
+using WIDESEA_Common.StockEnum;
+using WIDESEA_Common.TaskEnum;
+using WIDESEA_Core;
+using WIDESEA_Core.Helper;
+using WIDESEA_DTO.Task;
+using WIDESEA_Model.Models;
+
+namespace WIDESEA_TaskInfoService
+{
+    public partial class TaskService
+    {
+        #region 鑷姩鍑哄簱浠诲姟
+
+        /// <summary>
+        /// 鑷姩鍒涘缓鍑哄簱浠诲姟 - 鏌ヨ鍒版湡搴撳瓨骞跺垱寤轰换鍔�
+        /// </summary>
+        public async Task<WebResponseContent> CreateAutoOutboundTasksAsync()
+        {
+            try
+            {
+                // 1. 鏌ヨ鍒版湡搴撳瓨
+                var expiredStocks = await _stockInfoService.Repository
+                    .QueryDataNavAsync(s => s.OutboundDate <= DateTime.Now
+                        && s.StockStatus == StockStatusEmun.鍏ュ簱瀹屾垚.GetHashCode());
+
+                if (expiredStocks == null || !expiredStocks.Any())
+                {
+                    return WebResponseContent.Instance.OK("鏃犲埌鏈熷簱瀛橀渶瑕佸鐞�");
+                }
+
+                // 杩囨护鏈変綅缃笖浣嶇疆鏈夊簱瀛樼殑璁板綍
+                expiredStocks = expiredStocks
+                    .Where(s => s.LocationDetails != null
+                        && s.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode())
+                    .ToList();
+
+                if (!expiredStocks.Any())
+                {
+                    return WebResponseContent.Instance.OK("鏃犵鍚堟潯浠剁殑鍒版湡搴撳瓨");
+                }
+
+                // 2. 妫�鏌ュ凡瀛樺湪鐨勪换鍔�
+                var palletCodes = expiredStocks.Select(s => s.PalletCode).ToList();
+                var existingTasks = await Repository.QueryDataAsync(t =>
+                    palletCodes.Contains(t.PalletCode)
+                    && (t.TaskStatus == TaskStatusEnum.New.GetHashCode()
+                        || t.TaskStatus == TaskStatusEnum.SC_Executing.GetHashCode()
+                        || t.TaskStatus == TaskInStatusEnum.InNew.GetHashCode()));
+
+                var processedPallets = existingTasks.Select(t => t.PalletCode).ToHashSet();
+
+                // 3. 绛涢�夐渶瑕佸鐞嗙殑搴撳瓨
+                var stocksToProcess = expiredStocks
+                    .Where(s => !processedPallets.Contains(s.PalletCode))
+                    .ToList();
+
+                if (!stocksToProcess.Any())
+                {
+                    return WebResponseContent.Instance.OK("鎵�鏈夊埌鏈熷簱瀛樺凡瀛樺湪浠诲姟");
+                }
+
+                // 4. 鑾峰彇閰嶇疆鐨勭洰鏍囧湴鍧�鏄犲皠
+                var targetAddressMap = _configuration.GetSection("AutoOutboundTask:TargetAddresses")
+                    .Get<Dictionary<string, List<string>>>()
+                    ?? new Dictionary<string, List<string>>();
+
+                // 5. 鎵归噺鍒涘缓浠诲姟
+                var taskList = new List<Dt_Task>();
+                foreach (var stock in stocksToProcess)
+                {
+                    // 鏍规嵁宸烽亾纭畾鐩爣鍦板潃锛堜紭鍏堟牴鎹� Remark 纭畾锛孯emark 涓虹┖鍒欐牴鎹贩閬撻厤缃級
+                    var targetAddress = DetermineTargetAddressByRemark(
+                        stock.Remark ?? "",
+                        stock.LocationDetails?.RoadwayNo ?? "",
+                        targetAddressMap);
+
+                    var task = new Dt_Task
+                    {
+                        WarehouseId = stock.WarehouseId,
+                        PalletCode = stock.PalletCode,
+                        PalletType = stock.PalletType,
+                        SourceAddress = stock.LocationCode,
+                        CurrentAddress = stock.LocationCode,
+                        NextAddress = targetAddress,
+                        TargetAddress = targetAddress,
+                        Roadway = stock.LocationDetails?.RoadwayNo ?? "",
+                        TaskType = TaskTypeEnum.Outbound.GetHashCode(),
+                        TaskStatus = TaskStatusEnum.New.GetHashCode(),
+                        Grade = 1,
+                        TaskNum = await BaseDal.GetTaskNo(),
+                        Creater = "system_auto"
+                    };
+                    taskList.Add(task);
+                }
+
+                var transactionResult = await _unitOfWorkManage.BeginTranAsync(async () =>
+                {
+                    var addResult = await BaseDal.AddDataAsync(taskList) > 0;
+                    if (!addResult)
+                    {
+                        return WebResponseContent.Instance.Error($"鎵归噺鍒涘缓浠诲姟澶辫触锛屽叡 {taskList.Count} 涓换鍔�");
+                    }
+
+                    // 浠诲姟鍒涘缓鎴愬姛鍚庯紝鍚屾閿佸畾搴撳瓨鍜岃揣浣嶇姸鎬侊紝閬垮厤閲嶅鍒嗛厤
+                    var stocksToUpdate = stocksToProcess
+                        .Select(s =>
+                        {
+                            s.StockStatus = StockStatusEmun.鍑哄簱閿佸畾.GetHashCode();
+                            return s;
+                        })
+                        .ToList();
+
+                    var updateStockResult = await _stockInfoService.Repository.UpdateDataAsync(stocksToUpdate);
+                    if (!updateStockResult)
+                    {
+                        return WebResponseContent.Instance.Error($"浠诲姟鍒涘缓鎴愬姛锛屼絾搴撳瓨鐘舵�佹洿鏂板け璐ワ紝鍏� {stocksToUpdate.Count} 鏉�");
+                    }
+
+                    var locationsToUpdate = stocksToProcess
+                        .Where(s => s.LocationDetails != null)
+                        .GroupBy(s => s.LocationDetails.Id)
+                        .Select(g =>
+                        {
+                            var location = g.First().LocationDetails;
+                            location.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode();
+                            return location;
+                        })
+                        .ToList();
+
+                    if (locationsToUpdate.Any())
+                    {
+                        var updateLocationResult = await _locationInfoService.Repository.UpdateDataAsync(locationsToUpdate);
+                        if (!updateLocationResult)
+                        {
+                            return WebResponseContent.Instance.Error($"浠诲姟鍒涘缓鎴愬姛锛屼絾璐т綅鐘舵�佹洿鏂板け璐ワ紝鍏� {locationsToUpdate.Count} 鏉�");
+                        }
+                    }
+
+                    return WebResponseContent.Instance.OK();
+                });
+                if (!transactionResult.Status)
+                {
+                    return transactionResult;
+                }
+
+                // 6. 閫氱煡 WCS锛堝紓姝ワ紝涓嶅奖鍝嶄富娴佺▼锛�
+                _ = Task.Run(() =>
+                {
+                    try
+                    {
+                        var wmstaskDtos = _mapper.Map<List<WMSTaskDTO>>(taskList);
+                        _httpClientHelper.Post<WebResponseContent>(
+                            "http://localhost:9292/api/Task/ReceiveTask",
+                            wmstaskDtos.ToJson());
+                    }
+                    catch (Exception ex)
+                    {
+                        // WCS 閫氱煡澶辫触涓嶅奖鍝嶄换鍔″垱寤猴紝璁板綍鏃ュ織鍗冲彲
+                        Console.WriteLine($"WCS 鎵归噺閫氱煡澶辫触锛屼换鍔℃暟閲�: {taskList.Count}, 閿欒: {ex.Message}");
+                    }
+                });
+
+                return WebResponseContent.Instance.OK($"鎴愬姛鍒涘缓 {taskList.Count} 涓嚭搴撲换鍔�", taskList.Count);
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error($"鑷姩鍒涘缓鍑哄簱浠诲姟澶辫触: {ex.Message}");
+            }
+        }
+
+        #endregion 鑷姩鍑哄簱浠诲姟
+    }
+}
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Inbound.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Inbound.cs
new file mode 100644
index 0000000..0a1f053
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Inbound.cs
@@ -0,0 +1,210 @@
+using WIDESEA_Common.Constants;
+using WIDESEA_Common.LocationEnum;
+using WIDESEA_Common.StockEnum;
+using WIDESEA_Common.TaskEnum;
+using WIDESEA_Common.WareHouseEnum;
+using WIDESEA_Core;
+using WIDESEA_DTO.Task;
+using WIDESEA_Model.Models;
+
+namespace WIDESEA_TaskInfoService
+{
+    public partial class TaskService
+    {
+        #region 鍏ュ簱浠诲姟
+
+        /// <summary>
+        /// 鍒涘缓浠诲姟锛堢粍鐩樺叆搴撲换鍔°�佺┖鎵樼洏鍥炲簱浠诲姟锛�
+        /// </summary>
+        public async Task<WebResponseContent> CreateTaskInboundAsync(CreateTaskDto taskDto)
+        {
+            try
+            {
+                WebResponseContent content = await GetTaskByPalletCodeAsync(taskDto.PalletCode);
+                if (content.Status)
+                {
+                    return content;
+                }
+
+                if (string.IsNullOrWhiteSpace(taskDto.PalletCode) ||
+                    string.IsNullOrWhiteSpace(taskDto.Roadway))
+                {
+                    return WebResponseContent.Instance.Error("鏃犳晥鐨勪换鍔¤鎯�");
+                }
+
+                if (taskDto.TaskType != TaskTypeEnum.Inbound && taskDto.TaskType != TaskTypeEnum.InEmpty)
+                {
+                    return WebResponseContent.Instance.Error("鏃犳晥鐨勪换鍔¤鎯�");
+                }
+
+                // 浣跨敤 switch 琛ㄨ揪寮忔槧灏勪换鍔$被鍨�
+                int taskInboundType = taskDto.TaskType switch
+                {
+                    TaskTypeEnum.Inbound => TaskInboundTypeEnum.Inbound.GetHashCode(),
+                    TaskTypeEnum.InEmpty => TaskInboundTypeEnum.InEmpty.GetHashCode(),
+                    _ => 0 // 鐞嗚涓婁笉浼氳蛋鍒拌繖閲岋紝鍥犱负宸茬粡楠岃瘉杩囦簡
+                };
+
+                var task = new Dt_Task
+                {
+                    TaskNum = await BaseDal.GetTaskNo(),
+                    PalletCode = taskDto.PalletCode,
+                    PalletType = taskDto.PalletType,
+                    Roadway = taskDto.Roadway,
+                    TaskType = taskInboundType,
+                    TaskStatus = TaskInStatusEnum.InNew.GetHashCode(),
+                    SourceAddress = taskDto.SourceAddress,
+                    TargetAddress = taskDto.TargetAddress,
+                    CurrentAddress = taskDto.SourceAddress,
+                    NextAddress = taskDto.TargetAddress,
+                    WarehouseId = taskDto.WarehouseId,
+                    Grade = 1,
+                    Creater = "system"
+                };
+
+                var result = await Repository.AddDataAsync(task) > 0;
+                if (!result) return WebResponseContent.Instance.Error("浠诲姟鍒涘缓澶辫触");
+
+                var wmstaskDto = _mapper.Map<WMSTaskDTO>(task);
+                return WebResponseContent.Instance.OK("浠诲姟鍒涘缓鎴愬姛", wmstaskDto);
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error($"浠诲姟鍒涘缓澶辫触: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 鑾峰彇鍙叆搴撹揣浣�
+        /// </summary>
+        public async Task<WebResponseContent> GetTasksLocationAsync(CreateTaskDto taskDto)
+        {
+            try
+            {
+                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
+                if (task == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑浠诲姟");
+
+                var locationInfo = await _locationInfoService.GetLocationInfo(task.Roadway);
+                if (locationInfo == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑璐т綅");
+
+                return await _unitOfWorkManage.BeginTranAsync(async () =>
+                {
+                    locationInfo.LocationStatus = LocationStatusEnum.FreeLock.GetHashCode();
+                    task.CurrentAddress = task.SourceAddress;
+                    task.NextAddress = locationInfo.LocationCode;
+                    task.TargetAddress = locationInfo.LocationCode;
+                    task.TaskStatus = TaskInStatusEnum.Line_InFinish.GetHashCode();
+
+                    var updateTaskResult = await BaseDal.UpdateDataAsync(task);
+                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(locationInfo);
+                    if (!updateTaskResult || !updateLocationResult)
+                    {
+                        return WebResponseContent.Instance.Error("浠诲姟鏇存柊澶辫触");
+                    }
+
+                    return WebResponseContent.Instance.OK("浠诲姟鏇存柊鎴愬姛", locationInfo.LocationCode);
+                });
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error($"鑾峰彇浠诲姟澶辫触: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 鍏ュ簱浠诲姟瀹屾垚锛氭坊鍔犲簱瀛橈紝淇敼璐т綅鐘舵�侊紝鍒犻櫎浠诲姟鏁版嵁锛屾坊鍔犲巻鍙蹭换鍔℃暟鎹�
+        /// </summary>
+        public async Task<WebResponseContent> InboundFinishTaskAsync(CreateTaskDto taskDto)
+        {
+            try
+            {
+                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
+                if (task == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑浠诲姟");
+
+                var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress);
+                if (location == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑璐т綅");
+
+                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
+                if (stockInfo == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴斿簱瀛樹俊鎭�");
+
+                // 鍒ゆ柇鏄笉鏄瀬鍗峰簱浠诲姟
+                if (taskDto.WarehouseId == (int)WarehouseEnum.FJ1 || taskDto.WarehouseId == (int)WarehouseEnum.ZJ1)
+                {
+                    return await CompleteAgvInboundTaskAsync(taskDto);
+                }
+
+                return await _unitOfWorkManage.BeginTranAsync(async () =>
+                {
+                    WebResponseContent content = new WebResponseContent();
+                    stockInfo.LocationCode = location.LocationCode;
+                    stockInfo.LocationId = location.Id;
+
+                    SetOutboundDateByRoadway(task, stockInfo);
+
+                    stockInfo.StockStatus = StockStatusEmun.鍏ュ簱瀹屾垚.GetHashCode();
+
+                    location.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
+
+                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
+                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
+                    if (!updateLocationResult || !updateStockResult)
+                        return WebResponseContent.Instance.Error("浠诲姟瀹屾垚澶辫触");
+                    // 璋冪敤MES鎵樼洏杩涚珯
+                    //var inboundRequest = new InboundInContainerRequest
+                    //{
+                    //    EquipmentCode = "STK-GROUP-001",
+                    //    ResourceCode = "STK-GROUP-001",
+                    //    LocalTime = DateTime.Now,
+                    //    ContainerCode = taskDto.PalletCode
+                    //};
+                    //var inboundResult = _mesService.InboundInContainer(inboundRequest);
+                    //if (inboundResult == null || inboundResult.Data == null || !inboundResult.Data.IsSuccess)
+                    //{
+                    //    return content.Error($"浠诲姟瀹屾垚澶辫触锛歁ES杩涚珯澶辫触: {inboundResult?.Data?.Msg ?? inboundResult?.ErrorMessage ?? "鏈煡閿欒"}");
+                    //}
+                    return await CompleteTaskAsync(task, "鍏ュ簱瀹屾垚");
+                });
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error($"瀹屾垚浠诲姟澶辫触: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 鏍规嵁宸烽亾绫诲瀷璁剧疆搴撳瓨鐨勫嚭搴撴椂闂村拰澶囨敞
+        /// </summary>
+        /// <param name="task">浠诲姟淇℃伅</param>
+        /// <param name="stockInfo">搴撳瓨淇℃伅</param>
+        private void SetOutboundDateByRoadway(Dt_Task task, Dt_StockInfo stockInfo)
+        {
+            var now = DateTime.Now;
+            if (task.Roadway.Contains("GW"))
+            {
+                stockInfo.OutboundDate = string.IsNullOrEmpty(stockInfo.Remark)
+                    ? now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_GW1_FIRST)
+                    : stockInfo.Remark == StockRemarkConstants.GW1
+                        ? now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_GW1_SECOND)
+                        : now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_GW1_FIRST);
+
+                stockInfo.Remark = string.IsNullOrEmpty(stockInfo.Remark)
+                    ? StockRemarkConstants.GW1
+                    : stockInfo.Remark == StockRemarkConstants.GW1
+                        ? StockRemarkConstants.GW2
+                        : stockInfo.Remark;
+            }
+            else if (task.Roadway.Contains("CW"))
+            {
+                stockInfo.OutboundDate = now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_CW1);
+                if (stockInfo.Remark == StockRemarkConstants.GW2)
+                    stockInfo.Remark = StockRemarkConstants.CW1;
+            }
+            else
+            {
+                stockInfo.OutboundDate = now;
+            }
+        }
+
+        #endregion 鍏ュ簱浠诲姟
+    }
+}
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Manual.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Manual.cs
new file mode 100644
index 0000000..4c7144b
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Manual.cs
@@ -0,0 +1,328 @@
+using System.ComponentModel;
+using System.Reflection;
+using Newtonsoft.Json;
+using WIDESEA_Common.TaskEnum;
+using WIDESEA_Core;
+using WIDESEA_Core.Helper;
+using WIDESEA_DTO.Task;
+using WIDESEA_Model.Models;
+
+namespace WIDESEA_TaskInfoService
+{
+    public partial class TaskService
+    {
+        #region 鎵嬪姩浠诲姟
+
+        /// <summary>
+        /// 鎵嬪姩鍒涘缓浠诲姟
+        /// </summary>
+        /// <param name="dto">鎵嬪姩鍒涘缓浠诲姟鍙傛暟</param>
+        /// <returns></returns>
+        public async Task<WebResponseContent> CreateManualTaskAsync(CreateManualTaskDto dto)
+        {
+            try
+            {
+                int taskType;
+                int taskStatus;
+                switch (dto.TaskType)
+                {
+                    case "鍏ュ簱":
+                        taskType = TaskInboundTypeEnum.Inbound.GetHashCode();
+                        taskStatus = TaskInStatusEnum.InNew.GetHashCode();
+                        break;
+
+                    case "鍑哄簱":
+                        taskType = TaskOutboundTypeEnum.Outbound.GetHashCode();
+                        taskStatus = TaskOutStatusEnum.OutNew.GetHashCode();
+                        break;
+
+                    case "绉诲簱":
+                        taskType = TaskRelocationTypeEnum.Relocation.GetHashCode();
+                        taskStatus = TaskRelocationStatusEnum.RelocationNew.GetHashCode();
+                        break;
+
+                    default:
+                        return WebResponseContent.Instance.Error($"涓嶆敮鎸佺殑浠诲姟绫诲瀷: {dto.TaskType}");
+                }
+
+                int taskNum = await BaseDal.GetTaskNo();
+
+                var task = new Dt_Task
+                {
+                    TaskNum = taskNum,
+                    PalletCode = dto.Barcode,
+                    SourceAddress = dto.SourceAddress,
+                    TargetAddress = dto.TargetAddress,
+                    TaskType = taskType,
+                    TaskStatus = taskStatus,
+                    Grade = dto.Grade,
+                    Roadway = dto.TargetAddress,
+                    WarehouseId = dto.WarehouseId,
+                    CurrentAddress = dto.SourceAddress,
+                    NextAddress = dto.TargetAddress,
+                    Creater = "manual",
+                    CreateDate = DateTime.Now,
+                    ModifyDate = DateTime.Now
+                };
+
+                var wmsTaskDtos = new List<WMSTaskDTO>()
+                {
+                    new()
+                    {
+                        TaskNum = task.TaskNum,
+                        PalletCode = task.PalletCode,
+                        SourceAddress = task.SourceAddress,
+                        TargetAddress = task.TargetAddress,
+                        TaskType = task.TaskType,
+                        Roadway = task.Roadway,
+                        TaskStatus = task.TaskStatus,
+                        WarehouseId = task.WarehouseId
+                    }
+                };
+
+                return await _unitOfWorkManage.BeginTranAsync(async () =>
+                {
+                    // 淇濆瓨鍒版暟鎹簱骞跺悓姝ュ彂閫佺粰WCS
+                    var result = await BaseDal.AddDataAsync(task) > 0;
+                    if (!result)
+                        return WebResponseContent.Instance.Error("鍒涘缓浠诲姟澶辫触");
+
+                    var wcsResult = _httpClientHelper.Post<WebResponseContent>(
+                        "http://localhost:9292/api/Task/ReceiveManualTask",
+                        wmsTaskDtos.ToJson());
+
+                    if (!wcsResult.IsSuccess || !wcsResult.Data.Status)
+                        return WebResponseContent.Instance.Error($"浠诲姟宸插垱寤轰絾鍙戦�佺粰WCS澶辫触: {wcsResult.Data?.Message}");
+
+                    return WebResponseContent.Instance.OK($"鎵嬪姩鍒涘缓浠诲姟鎴愬姛锛屼换鍔″彿: {taskNum}");
+                });
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error($"鎵嬪姩鍒涘缓浠诲姟寮傚父: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 鎵嬪姩涓嬪彂浠诲姟鍒癢CS锛堟壒閲忓鐞嗭級
+        /// </summary>
+        /// <param name="dtos">涓嬪彂浠诲姟鍙傛暟鍒楄〃</param>
+        /// <returns>鎵归噺涓嬪彂缁撴灉</returns>
+        public async Task<WebResponseContent> DispatchTasksToWCSAsync(List<DispatchTaskDto> dtos)
+        {
+            try
+            {
+                if (dtos == null || !dtos.Any())
+                    return WebResponseContent.Instance.Error("璇烽�夋嫨瑕佷笅鍙戠殑浠诲姟");
+
+                var resultDto = new DispatchResultDto
+                {
+                    SuccessCount = 0,
+                    FailCount = 0,
+                    FailResults = new List<DispatchTaskResultDto>()
+                };
+
+                // 绗竴姝ワ細鏌ヨ骞舵牎楠屾墍鏈変换鍔$姸鎬侊紝鏀堕泦鏈夋晥浠诲姟
+                var validTasks = new List<(DispatchTaskDto Dto, Dt_Task Task)>();
+
+                foreach (var dto in dtos)
+                {
+                    var task = await BaseDal.QueryFirstAsync(t => t.TaskId == dto.TaskId);
+                    if (task == null)
+                    {
+                        resultDto.FailResults.Add(new DispatchTaskResultDto
+                        {
+                            TaskId = dto.TaskId,
+                            TaskNum = 0,
+                            Success = false,
+                            ErrorMessage = "浠诲姟涓嶅瓨鍦�"
+                        });
+                        resultDto.FailCount++;
+                        continue;
+                    }
+
+                    // 鏍¢獙浠诲姟鐘舵�侊細浠呭叆搴撴柊鍗�/鍑哄簱鏂板崟/绉诲簱鏂板崟鍙笅鍙�
+                    bool canDispatch = false;
+                    if (task.TaskType == TaskInboundTypeEnum.Inbound.GetHashCode()
+                        && task.TaskStatus == TaskInStatusEnum.InNew.GetHashCode())
+                        canDispatch = true;
+                    else if (task.TaskType == TaskOutboundTypeEnum.Outbound.GetHashCode()
+                        && task.TaskStatus == TaskOutStatusEnum.OutNew.GetHashCode())
+                        canDispatch = true;
+                    else if (task.TaskType == TaskRelocationTypeEnum.Relocation.GetHashCode()
+                        && task.TaskStatus == TaskRelocationStatusEnum.RelocationNew.GetHashCode())
+                        canDispatch = true;
+
+                    if (!canDispatch)
+                    {
+                        var statusName = GetTaskStatusName(task.TaskType, task.TaskStatus);
+                        resultDto.FailResults.Add(new DispatchTaskResultDto
+                        {
+                            TaskId = dto.TaskId,
+                            TaskNum = task.TaskNum,
+                            Success = false,
+                            ErrorMessage = $"浠诲姟鐘舵�乕{statusName}]涓嶅厑璁镐笅鍙�"
+                        });
+                        resultDto.FailCount++;
+                        continue;
+                    }
+
+                    validTasks.Add((dto, task));
+                }
+
+                // 濡傛灉鍏ㄩ儴鏍¢獙澶辫触锛岀洿鎺ヨ繑鍥�
+                if (validTasks.Count == 0)
+                    return WebResponseContent.Instance.Error($"涓嬪彂澶辫触锛屽叡{resultDto.FailCount}涓换鍔�");
+
+                // 绗簩姝ワ細鏋勯�犳墍鏈塛MSTaskDTO锛屼竴娆℃�ц皟鐢╓CS
+                var wmsTaskDtos = validTasks.Select(vt => new WMSTaskDTO
+                {
+                    Id = vt.Task.TaskId,
+                    TaskNum = vt.Task.TaskNum,
+                    PalletCode = vt.Dto.PalletCode,
+                    SourceAddress = vt.Dto.SourceAddress,
+                    TargetAddress = vt.Dto.TargetAddress,
+                    CurrentAddress = vt.Dto.SourceAddress,
+                    NextAddress = vt.Dto.TargetAddress,
+                    TaskType = vt.Task.TaskType,
+                    TaskStatus = vt.Task.TaskStatus,
+                    Roadway = vt.Task.Roadway,
+                    Grade = vt.Dto.Grade,
+                    WarehouseId = vt.Task.WarehouseId,
+                    PalletType = vt.Task.PalletType
+                }).ToList();
+
+                // 涓�娆℃�ц皟鐢╓CS鎵归噺鎺ュ彛
+                var wcsResult = _httpClientHelper.Post<WebResponseContent>(
+                    "http://localhost:9292/api/Task/ReceiveManualTask",
+                    wmsTaskDtos.ToJson());
+
+                if (wcsResult == null || !wcsResult.IsSuccess)
+                {
+                    // WCS璋冪敤澶辫触锛屾墍鏈変换鍔¢兘绠楀け璐�
+                    // 灏濊瘯浠嶹CS鍝嶅簲涓В鏋愰敊璇鎯�
+                    string errorDetail = "";
+                    if (wcsResult?.Data != null)
+                    {
+                        // 灏濊瘯灏咲ata杞崲涓洪敊璇俊鎭�
+                        try
+                        {
+                            var errorData = wcsResult.Data.ToString();
+                            errorDetail = $"WCS閿欒: {errorData}";
+                        }
+                        catch
+                        {
+                            errorDetail = wcsResult?.ErrorMessage ?? "WCS鍝嶅簲寮傚父";
+                        }
+                    }
+                    else
+                    {
+                        errorDetail = wcsResult?.ErrorMessage ?? "WCS鍝嶅簲寮傚父";
+                    }
+
+                    foreach (var vt in validTasks)
+                    {
+                        resultDto.FailResults.Add(new DispatchTaskResultDto
+                        {
+                            TaskId = vt.Dto.TaskId,
+                            TaskNum = vt.Task.TaskNum,
+                            Success = false,
+                            ErrorMessage = $"{errorDetail} (浠诲姟鍙�:{vt.Task.TaskNum}, 鎵樼洏:{vt.Dto.PalletCode})"
+                        });
+                        resultDto.FailCount++;
+                    }
+                    resultDto.SuccessCount = 0;
+                    return WebResponseContent.Instance.Error($"WCS鎵归噺涓嬪彂澶辫触锛屽叡{resultDto.FailCount}涓换鍔�");
+                }
+
+                // WCS璋冪敤鎴愬姛锛岃В鏋愯繑鍥炵殑缁撴瀯鍖栨暟鎹�
+                ReceiveTaskResultDto wcsResultData = null;
+                try
+                {
+                    if (wcsResult.Data?.Data != null)
+                    {
+                        var jsonStr = wcsResult.Data.Data.ToString();
+                        if (!string.IsNullOrEmpty(jsonStr) && jsonStr.Contains("duplicateTasks"))
+                        {
+                            wcsResultData = JsonConvert.DeserializeObject<ReceiveTaskResultDto>(jsonStr);
+                        }
+                    }
+                }
+                catch (Exception ex)
+                {
+                    // 瑙f瀽WCS杩斿洖鏁版嵁澶辫触锛岃褰曟棩蹇椾絾缁х画澶勭悊
+                    Console.WriteLine($"瑙f瀽WCS杩斿洖鏁版嵁寮傚父: {ex.Message}");
+                }
+
+                // 濡傛灉鏈夐噸澶嶄换鍔★紝璁板綍鍒板け璐ョ粨鏋滀腑
+                if (wcsResultData?.DuplicateTasks != null && wcsResultData.DuplicateTasks.Count > 0)
+                {
+                    foreach (var dup in wcsResultData.DuplicateTasks)
+                    {
+                        var statusName = GetTaskStatusName(dup.TaskType, dup.TaskStatus);
+                        resultDto.FailResults.Add(new DispatchTaskResultDto
+                        {
+                            TaskId = 0, // 閲嶅浠诲姟鍙兘娌℃湁WMS鐨凾askId
+                            TaskNum = dup.TaskNum,
+                            Success = false,
+                            ErrorMessage = $"WCS涓凡瀛樺湪璇ヤ换鍔�(鎵樼洏:{dup.PalletCode}, 鐘舵��:{statusName})"
+                        });
+                        resultDto.FailCount++;
+                    }
+                }
+
+                // 绗笁姝ワ細WCS璋冪敤鎴愬姛鍚庯紝鎵归噺鏇存柊DB
+                foreach (var vt in validTasks)
+                {
+                    vt.Task.PalletCode = vt.Dto.PalletCode;
+                    vt.Task.SourceAddress = vt.Dto.SourceAddress;
+                    vt.Task.TargetAddress = vt.Dto.TargetAddress;
+                    vt.Task.CurrentAddress = vt.Dto.SourceAddress;
+                    vt.Task.NextAddress = vt.Dto.TargetAddress;
+                    vt.Task.Grade = vt.Dto.Grade;
+                    vt.Task.Dispatchertime = DateTime.Now;
+                }
+
+                // 鎵归噺鏇存柊DB
+                var tasksToUpdate = validTasks.Select(vt => vt.Task).ToList();
+                await BaseDal.UpdateDataAsync(tasksToUpdate);
+
+                resultDto.SuccessCount = validTasks.Count - (wcsResultData?.DuplicateTasks?.Count ?? 0);
+
+                if (resultDto.FailCount == 0)
+                    return WebResponseContent.Instance.OK($"鎴愬姛涓嬪彂{resultDto.SuccessCount}涓换鍔�", resultDto);
+
+                var errorResponse = WebResponseContent.Instance.Error($"閮ㄥ垎涓嬪彂鎴愬姛{resultDto.SuccessCount}涓紝澶辫触{resultDto.FailCount}涓�");
+                errorResponse.Data = resultDto;
+                return errorResponse;
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error($"涓嬪彂浠诲姟寮傚父: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 鑾峰彇浠诲姟鐘舵�佸悕绉�
+        /// </summary>
+        /// <param name="taskType">浠诲姟绫诲瀷</param>
+        /// <param name="taskStatus">浠诲姟鐘舵��</param>
+        /// <returns>鐘舵�佺殑涓枃鎻忚堪</returns>
+        private string GetTaskStatusName(int taskType, int taskStatus)
+        {
+            FieldInfo? fieldInfo = taskType switch
+            {
+                _ when taskType == TaskInboundTypeEnum.Inbound.GetHashCode() => typeof(TaskInStatusEnum).GetField(((TaskInStatusEnum)taskStatus).ToString()),
+                _ when taskType == TaskOutboundTypeEnum.Outbound.GetHashCode() => typeof(TaskOutStatusEnum).GetField(((TaskOutStatusEnum)taskStatus).ToString()),
+                _ when taskType == TaskRelocationTypeEnum.Relocation.GetHashCode() => typeof(TaskRelocationStatusEnum).GetField(((TaskRelocationStatusEnum)taskStatus).ToString()),
+                _ => null
+            };
+
+            var descAttr = fieldInfo?.GetCustomAttributes(typeof(DescriptionAttribute), false)
+                .FirstOrDefault() as DescriptionAttribute;
+            return descAttr?.Description ?? taskStatus.ToString();
+        }
+
+        #endregion 鎵嬪姩浠诲姟
+    }
+}
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Outbound.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Outbound.cs
new file mode 100644
index 0000000..1a085ab
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Outbound.cs
@@ -0,0 +1,140 @@
+using WIDESEA_Common.Constants;
+using WIDESEA_Common.LocationEnum;
+using WIDESEA_Common.StockEnum;
+using WIDESEA_Common.TaskEnum;
+using WIDESEA_Common.WareHouseEnum;
+using WIDESEA_Core;
+using WIDESEA_DTO.Task;
+using WIDESEA_Model.Models;
+
+namespace WIDESEA_TaskInfoService
+{
+    public partial class TaskService
+    {
+        #region 鍑哄簱浠诲姟
+
+        /// <summary>
+        /// 鏍规嵁鎸囧畾鐨勪换鍔¤鎯呭紓姝ュ垱寤烘柊鐨勫嚭搴撲换鍔�
+        /// </summary>
+        public async Task<WebResponseContent> CreateTaskOutboundAsync(CreateTaskDto taskDto)
+        {
+            try
+            {
+                var stockResult = await _stockInfoService.GetStockInfoAsync(taskDto.WarehouseId);
+                if (stockResult == null || !stockResult.Any())
+                    return WebResponseContent.Instance.Error("鏈壘鍒板簱瀛樹俊鎭�");
+
+                var taskList = stockResult.Select(item => new Dt_Task
+                {
+                    WarehouseId = item.WarehouseId,
+                    PalletCode = item.PalletCode,
+                    PalletType = item.PalletType,
+                    SourceAddress = item.LocationCode,
+                    TargetAddress = taskDto.TargetAddress,
+                    Roadway = item.LocationDetails.RoadwayNo,
+                    TaskType = TaskTypeEnum.Outbound.GetHashCode(),
+                    TaskStatus = TaskStatusEnum.New.GetHashCode(),
+                    Grade = 1,
+                    TaskNum = 0,
+                    CurrentAddress = item.LocationCode,
+                    NextAddress = taskDto.TargetAddress,
+                    Creater = "system",
+                }).ToList();
+
+                var result = await BaseDal.AddDataAsync(taskList) > 0;
+                var wmstaskDto = result ? _mapper.Map<WMSTaskDTO>(taskList) : null;
+                return WebResponseContent.Instance.OK(result ? "浠诲姟鍒涘缓鎴愬姛" : "浠诲姟鍒涘缓澶辫触", wmstaskDto ?? new object());
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error($"浠诲姟鍒涘缓澶辫触: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 鍑哄簱浠诲姟瀹屾垚 锛氫慨鏀瑰簱瀛橈紝淇敼璐т綅鐘舵�侊紝鍒犻櫎浠诲姟鏁版嵁锛屾坊鍔犲巻鍙蹭换鍔℃暟鎹�
+        /// </summary>
+        public async Task<WebResponseContent> OutboundFinishTaskAsync(CreateTaskDto taskDto)
+        {
+            try
+            {
+                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
+                if (task == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑浠诲姟");
+
+                var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress);
+                if (location == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑璐т綅");
+
+                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
+                if (stockInfo == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴斿簱瀛樹俊鎭�");
+
+                // 鍒ゆ柇鏄笉鏄瀬鍗峰簱浠诲姟
+                if (taskDto.WarehouseId == (int)WarehouseEnum.FJ1 || taskDto.WarehouseId == (int)WarehouseEnum.ZJ1)
+                {
+                    OutTaskCompleteDto outTaskCompleteDto = new OutTaskCompleteDto()
+                    {
+                        TaskId = task.OrderNo ?? string.Empty,
+                        DevId = task.TargetAddress ?? string.Empty,
+                        ReqTime = DateTime.Now.ToString()
+                    };
+                    return await OutTaskComplete(outTaskCompleteDto);
+                }
+
+                WebResponseContent content = new WebResponseContent();
+                return await _unitOfWorkManage.BeginTranAsync(async () =>
+                {
+                    stockInfo.LocationId = 0;
+                    stockInfo.LocationCode = string.Empty;
+                    stockInfo.OutboundDate = DateTime.Now;
+
+                    location.LocationStatus = LocationStatusEnum.Free.GetHashCode();
+
+                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
+                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
+                    if (!updateLocationResult || !updateStockResult)
+                        return WebResponseContent.Instance.Error("浠诲姟瀹屾垚澶辫触");
+
+                    // 楂樻俯2鍙峰嚭搴撳埌CWSC1鏃讹紝鑷姩鍒涘缓鍏ュ簱浠诲姟鍒板父娓�1鍙峰贩閬�
+                    WMSTaskDTO? inboundTaskDto = null;
+                    if (task.TargetAddress == TaskAddressConstants.GW2_ADDRESS)
+                    {
+                        var inboundTask = new Dt_Task
+                        {
+                            TaskNum = await BaseDal.GetTaskNo(),
+                            PalletCode = task.PalletCode,
+                            PalletType = task.PalletType,
+                            Roadway = "CW1",
+                            TaskType = TaskInboundTypeEnum.Inbound.GetHashCode(),
+                            TaskStatus = TaskInStatusEnum.InNew.GetHashCode(),
+                            SourceAddress = task.TargetAddress,
+                            TargetAddress = task.TargetAddress,
+                            CurrentAddress = task.TargetAddress,
+                            NextAddress = task.TargetAddress,
+                            WarehouseId = (int)WarehouseEnum.CW1,
+                            Grade = 1,
+                            Creater = "system_auto"
+                        };
+                        await Repository.AddDataAsync(inboundTask);
+                        inboundTaskDto = _mapper.Map<WMSTaskDTO>(inboundTask);
+                    }
+
+                    var completeResult = await CompleteTaskAsync(task, "鍑哄簱瀹屾垚");
+                    if (!completeResult.Status)
+                        return completeResult;
+
+                    // 杩斿洖鍏ュ簱浠诲姟淇℃伅锛堝鏋滄湁锛�
+                    if (inboundTaskDto != null)
+                    {
+                        return content.OK("鍑哄簱瀹屾垚锛屽凡鍒涘缓鍏ュ簱浠诲姟", inboundTaskDto);
+                    }
+                    return content.OK("鍑哄簱瀹屾垚");
+                });
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error($"瀹屾垚浠诲姟澶辫触: {ex.Message}");
+            }
+        }
+
+        #endregion 鍑哄簱浠诲姟
+    }
+}
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Relocation.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Relocation.cs
new file mode 100644
index 0000000..ec5cb44
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Relocation.cs
@@ -0,0 +1,61 @@
+using WIDESEA_Common.LocationEnum;
+using WIDESEA_Common.StockEnum;
+using WIDESEA_Common.TaskEnum;
+using WIDESEA_Core;
+using WIDESEA_DTO.Task;
+
+namespace WIDESEA_TaskInfoService
+{
+    public partial class TaskService
+    {
+        #region 绉诲簱浠诲姟
+
+        /// <summary>
+        /// 绉诲簱浠诲姟瀹屾垚锛氫慨鏀瑰簱瀛樹綅缃笌鐘舵�侊紝淇敼婧�/鐩爣璐т綅鐘舵�侊紝鍒犻櫎浠诲姟鏁版嵁
+        /// </summary>
+        public async Task<WebResponseContent> RelocationFinishTaskAsync(CreateTaskDto taskDto)
+        {
+            try
+            {
+                var task = await BaseDal.QueryFirstAsync(s =>
+                    s.PalletCode == taskDto.PalletCode &&
+                    s.TaskType == TaskRelocationTypeEnum.Relocation.GetHashCode());
+                if (task == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑绉诲簱浠诲姟");
+
+                var sourceLocation = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress);
+                if (sourceLocation == null) return WebResponseContent.Instance.Error("鏈壘鍒扮Щ搴撴簮璐т綅");
+
+                var targetLocation = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress);
+                if (targetLocation == null) return WebResponseContent.Instance.Error("鏈壘鍒扮Щ搴撶洰鏍囪揣浣�");
+
+                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
+                if (stockInfo == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴斿簱瀛樹俊鎭�");
+
+                return await _unitOfWorkManage.BeginTranAsync(async () =>
+                {
+                    stockInfo.LocationCode = targetLocation.LocationCode;
+                    stockInfo.LocationId = targetLocation.Id;
+                    stockInfo.StockStatus = StockStatusEmun.鍏ュ簱瀹屾垚.GetHashCode();
+
+                    sourceLocation.LocationStatus = LocationStatusEnum.Free.GetHashCode();
+                    targetLocation.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
+
+                    var updateSourceResult = await _locationInfoService.UpdateLocationInfoAsync(sourceLocation);
+                    var updateTargetResult = await _locationInfoService.UpdateLocationInfoAsync(targetLocation);
+                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
+
+                    if (!updateSourceResult || !updateTargetResult || !updateStockResult)
+                        return WebResponseContent.Instance.Error("绉诲簱浠诲姟瀹屾垚澶辫触");
+
+                    return await CompleteTaskAsync(task, "绉诲簱瀹屾垚");
+                });
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error($"瀹屾垚浠诲姟澶辫触: {ex.Message}");
+            }
+        }
+
+        #endregion 绉诲簱浠诲姟
+    }
+}
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Robot.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Robot.cs
new file mode 100644
index 0000000..82b24b2
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Robot.cs
@@ -0,0 +1,147 @@
+using WIDESEA_Common.TaskEnum;
+using WIDESEA_Core;
+using WIDESEA_DTO.Stock;
+using WIDESEA_DTO.Task;
+using WIDESEA_Model.Models;
+
+namespace WIDESEA_TaskInfoService
+{
+    public partial class TaskService
+    {
+        #region 鏈烘鎵嬩换鍔�
+
+        /// <summary>
+        /// 鍒涘缓鏈烘鎵嬬粍鐩樹换鍔�
+        /// </summary>
+        public async Task<WebResponseContent> CreateRobotGroupPalletTaskAsync(StockDTO stock)
+        {
+            return await CreateRobotPalletTaskAsync(
+                stock,
+                "缁勭洏",
+                RobotTaskTypeEnum.GroupPallet,
+                s => string.IsNullOrWhiteSpace(s.TargetPalletNo) ? s.SourcePalletNo : s.TargetPalletNo,
+                requireStockWithoutLocation: false);
+        }
+
+        /// <summary>
+        /// 鍒涘缓鏈烘鎵嬫崲鐩樹换鍔�
+        /// </summary>
+        public async Task<WebResponseContent> CreateRobotChangePalletTaskAsync(StockDTO stock)
+        {
+            return await CreateRobotPalletTaskAsync(
+                stock,
+                "鎹㈢洏",
+                RobotTaskTypeEnum.ChangePallet,
+                s => s.SourcePalletNo,
+                requireStockWithoutLocation: true,
+                stockPalletCodeSelector: s => s.SourcePalletNo);
+        }
+
+        /// <summary>
+        /// 鍒涘缓鏈烘鎵嬫媶鐩樹换鍔�
+        /// </summary>
+        public async Task<WebResponseContent> CreateRobotSplitPalletTaskAsync(StockDTO stock)
+        {
+            return await CreateRobotPalletTaskAsync(
+                stock,
+                "鎷嗙洏",
+                RobotTaskTypeEnum.SplitPallet,
+                s => s.SourcePalletNo,
+                requireStockWithoutLocation: true,
+                stockPalletCodeSelector: s => s.SourcePalletNo);
+        }
+
+        /// <summary>
+        /// 鍒涘缓鏈烘鎵嬩换鍔$殑閫氱敤鏂规硶
+        /// </summary>
+        /// <param name="stock">搴撳瓨淇℃伅</param>
+        /// <param name="taskName">浠诲姟鍚嶇О锛堢粍鐩�/鎹㈢洏/鎷嗙洏锛�</param>
+        /// <param name="taskType">鏈烘鎵嬩换鍔$被鍨�</param>
+        /// <param name="palletCodeSelector">鎵樼洏鍙烽�夋嫨鍣�</param>
+        /// <param name="requireStockWithoutLocation">鏄惁瑕佹眰搴撳瓨鏈粦瀹氳揣浣�</param>
+        /// <param name="stockPalletCodeSelector">搴撳瓨鏌ヨ鐢ㄧ殑鎵樼洏鍙烽�夋嫨鍣�</param>
+        /// <returns>鍒涘缓缁撴灉</returns>
+        private async Task<WebResponseContent> CreateRobotPalletTaskAsync(
+            StockDTO stock,
+            string taskName,
+            RobotTaskTypeEnum taskType,
+            Func<StockDTO, string?> palletCodeSelector,
+            bool requireStockWithoutLocation,
+            Func<StockDTO, string?>? stockPalletCodeSelector = null)
+        {
+            try
+            {
+                if (stock == null)
+                    return WebResponseContent.Instance.Error("浠诲姟鍙傛暟涓嶈兘涓虹┖");
+
+                var palletCode = palletCodeSelector(stock)?.Trim();
+                if (string.IsNullOrWhiteSpace(palletCode))
+                    return WebResponseContent.Instance.Error("鎵樼洏鍙蜂笉鑳戒负绌�");
+
+                var sourceLineNo = stock.SourceLineNo?.Trim();
+                var targetLineNo = stock.TargetLineNo?.Trim();
+                if (string.IsNullOrWhiteSpace(sourceLineNo) || string.IsNullOrWhiteSpace(targetLineNo))
+                    return WebResponseContent.Instance.Error("鏉ユ簮绾夸綋缂栧彿鍜岀洰鏍囩嚎浣撶紪鍙蜂笉鑳戒负绌�");
+
+                var existingTask = await BaseDal.QueryFirstAsync(t =>
+                    t.PalletCode == palletCode &&
+                    (t.TaskStatus == TaskRobotStatusEnum.RobotNew.GetHashCode()
+                     || t.TaskStatus == TaskRobotStatusEnum.RobotExecuting.GetHashCode()
+                     || t.TaskStatus == TaskRobotStatusEnum.RobotPickFinish.GetHashCode()
+                     || t.TaskStatus == TaskRobotStatusEnum.RobotPutFinish.GetHashCode()
+                     || t.TaskStatus == TaskRobotStatusEnum.RobotPending.GetHashCode()));
+                if (existingTask != null)
+                    return WebResponseContent.Instance.Error($"鎵樼洏[{palletCode}]宸插瓨鍦ㄦ湭瀹屾垚浠诲姟");
+
+                Dt_StockInfo? stockInfo = null;
+                if (requireStockWithoutLocation)
+                {
+                    var stockPalletCode = (stockPalletCodeSelector ?? palletCodeSelector).Invoke(stock)?.Trim();
+                    if (string.IsNullOrWhiteSpace(stockPalletCode))
+                        return WebResponseContent.Instance.Error("婧愭墭鐩樺彿涓嶈兘涓虹┖");
+
+                    stockInfo = await _stockInfoService.GetStockInfoAsync(stockPalletCode);
+                    if (stockInfo == null)
+                        return WebResponseContent.Instance.Error($"鎵樼洏[{stockPalletCode}]搴撳瓨涓嶅瓨鍦�");
+
+                    if (stockInfo.LocationId > 0 || !string.IsNullOrWhiteSpace(stockInfo.LocationCode))
+                        return WebResponseContent.Instance.Error($"鎵樼洏[{stockPalletCode}]搴撳瓨宸茬粦瀹氳揣浣嶏紝涓嶈兘鍒涘缓鏈烘鎵媨taskName}浠诲姟");
+                }
+                var section = App.Configuration.GetSection("RobotTaskAddressRules").GetSection(targetLineNo).GetChildren().Select(c => c.Value).ToArray();
+                if (section.Length < 2)
+                    return WebResponseContent.Instance.Error($"鏈壘鍒扮嚎浣揫{targetLineNo}]鐨勫湴鍧�閰嶇疆");
+
+                var task = new Dt_Task
+                {
+                    TaskNum = await BaseDal.GetTaskNo(),
+                    PalletCode = palletCode,
+                    PalletType = stockInfo?.PalletType ?? 0,
+                    Roadway = stock.Roadway ?? string.Empty,
+                    TaskType = taskType.GetHashCode(),
+                    TaskStatus = TaskRobotStatusEnum.RobotNew.GetHashCode(),
+                    SourceAddress = section[0]!,
+                    TargetAddress = section[1]!,
+                    CurrentAddress = targetLineNo,
+                    NextAddress = targetLineNo,
+                    WarehouseId = stockInfo?.WarehouseId ?? 1,
+                    Grade = 1,
+                    Remark = $"鏈烘鎵媨taskName}",
+                    Creater = "system"
+                };
+
+                var result = await Repository.AddDataAsync(task) > 0;
+                if (!result)
+                    return WebResponseContent.Instance.Error($"鏈烘鎵媨taskName}浠诲姟鍒涘缓澶辫触");
+
+                var wmstaskDto = _mapper.Map<WMSTaskDTO>(task) ?? new WMSTaskDTO();
+                return WebResponseContent.Instance.OK($"鏈烘鎵媨taskName}浠诲姟鍒涘缓鎴愬姛", wmstaskDto);
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error($"鏈烘鎵媨taskName}浠诲姟鍒涘缓澶辫触: {ex.Message}");
+            }
+        }
+
+        #endregion 鏈烘鎵嬩换鍔�
+    }
+}
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_TaskStatus.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_TaskStatus.cs
new file mode 100644
index 0000000..2ab0add
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_TaskStatus.cs
@@ -0,0 +1,39 @@
+using WIDESEA_Common.TaskEnum;
+using WIDESEA_Core;
+using WIDESEA_DTO.Task;
+
+namespace WIDESEA_TaskInfoService
+{
+    public partial class TaskService
+    {
+        #region 浠诲姟鐘舵�佺鐞�
+
+        /// <summary>
+        /// 淇敼浠诲姟鐘舵�侊紙鏍规嵁浠诲姟ID淇敼涓烘寚瀹氱姸鎬侊級
+        /// </summary>
+        /// <param name="taskDto">浠诲姟鐘舵�佹洿鏂板弬鏁�</param>
+        /// <returns>淇敼缁撴灉</returns>
+        public async Task<WebResponseContent> UpdateTaskByStatusAsync(UpdateTaskDto taskDto)
+        {
+            try
+            {
+                var taskInfo = await BaseDal.QueryFirstAsync(s => s.TaskNum == taskDto.Id);
+                if (taskInfo == null)
+                    return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑浠诲姟");
+
+                taskInfo.TaskStatus = taskDto.NewStatus;
+                taskInfo.NextAddress = taskDto.NextAddress;
+                taskInfo.CurrentAddress = taskDto.CurrentAddress;
+                await BaseDal.UpdateDataAsync(taskInfo);
+
+                return WebResponseContent.Instance.OK("淇敼鎴愬姛", taskInfo);
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error($"淇敼澶辫触: {ex.Message}");
+            }
+        }
+
+        #endregion 浠诲姟鐘舵�佺鐞�
+    }
+}
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Tray.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Tray.cs
new file mode 100644
index 0000000..51d6888
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Tray.cs
@@ -0,0 +1,197 @@
+using Mapster;
+using WIDESEA_Common.LocationEnum;
+using WIDESEA_Common.StockEnum;
+using WIDESEA_Common.TaskEnum;
+using WIDESEA_Core;
+using WIDESEA_DTO.Task;
+using WIDESEA_Model.Models;
+
+namespace WIDESEA_TaskInfoService
+{
+    public partial class TaskService
+    {
+        #region 绌烘墭鐩樹换鍔�
+
+        /// <summary>
+        /// 鍒涘缓绌烘墭鐩樺叆搴撲换鍔�
+        /// </summary>
+        /// <param name="taskDto"></param>
+        /// <returns></returns>
+        public async Task<WebResponseContent> CreateTaskInboundTrayAsync(CreateTaskDto taskDto)
+        {
+            try
+            {
+                WebResponseContent content = await GetTaskByPalletCodeAsync(taskDto.PalletCode);
+                if (content.Status)
+                {
+                    return content;
+                }
+
+                return WebResponseContent.Instance.OK("鏌ヨ鎴愬姛");
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error($"鏌ヨ浠诲姟澶辫触: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 绌烘墭鐩樺叆搴撳畬鎴�
+        /// </summary>
+        public async Task<WebResponseContent> InboundFinishTaskTrayAsync(CreateTaskDto taskDto)
+        {
+            try
+            {
+                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
+                if (task == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑浠诲姟");
+
+                var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress);
+                if (location == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑璐т綅");
+
+                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
+                if (stockInfo == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴斿簱瀛樹俊鎭�");
+
+                return await _unitOfWorkManage.BeginTranAsync(async () =>
+                {
+                    stockInfo.LocationCode = location.LocationCode;
+                    stockInfo.LocationId = location.Id;
+                    stockInfo.StockStatus = StockStatusEmun.绌烘墭鐩樺簱瀛�.GetHashCode();
+
+                    location.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
+
+                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
+                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
+                    if (!updateLocationResult || !updateStockResult)
+                        return WebResponseContent.Instance.Error("浠诲姟瀹屾垚澶辫触");
+
+                    var saveTaskHistoryResult = await SaveTaskHistoryAsync(task, "绌烘墭鐩樺叆搴撳畬鎴�");
+                    if (!saveTaskHistoryResult.Status)
+                        return saveTaskHistoryResult;
+
+                    var saveStockHistoryResult = await SaveStockHistoryAsync(stockInfo, "绌烘墭鐩樺叆搴撳畬鎴�");
+                    if (!saveStockHistoryResult.Status)
+                        return saveStockHistoryResult;
+
+                    var deleteResult = await BaseDal.DeleteDataAsync(task);
+                    if (!deleteResult) return WebResponseContent.Instance.Error("浠诲姟瀹屾垚澶辫触");
+
+                    return WebResponseContent.Instance.OK("浠诲姟瀹屾垚");
+                });
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error($"瀹屾垚浠诲姟澶辫触: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 鍒涘缓绌烘墭鐩樺嚭搴撲换鍔�
+        /// </summary>
+        /// <param name="taskDto"></param>
+        /// <returns></returns>
+        public async Task<WebResponseContent> GetOutBoundTrayTaskAsync(CreateTaskDto taskDto)
+        {
+            try
+            {
+                var dt_Task = await BaseDal.QueryFirstAsync(x => x.TargetAddress == taskDto.TargetAddress);
+                if (dt_Task != null)
+                {
+                    var taskDTO = dt_Task.Adapt<WMSTaskDTO>();
+                    return WebResponseContent.Instance.OK("浠诲姟鍒涘缓鎴愬姛", taskDTO);
+                }
+
+                var stockInfo = await _stockInfoService.Repository.QueryDataNavFirstAsync(x => x.LocationDetails.WarehouseId == taskDto.WarehouseId && x.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode() && x.StockStatus == StockStatusEmun.绌烘墭鐩樺簱瀛�.GetHashCode());
+                if (stockInfo == null)
+                    return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑搴撳瓨淇℃伅");
+
+                var task = new Dt_Task()
+                {
+                    WarehouseId = stockInfo.LocationDetails.WarehouseId,
+                    PalletCode = stockInfo.PalletCode,
+                    PalletType = stockInfo.PalletType,
+                    SourceAddress = stockInfo.LocationCode,
+                    CurrentAddress = stockInfo.LocationCode,
+                    NextAddress = taskDto.TargetAddress,
+                    TargetAddress = taskDto.TargetAddress,
+                    Roadway = stockInfo.LocationDetails.RoadwayNo,
+                    TaskType = TaskOutboundTypeEnum.OutEmpty.GetHashCode(),
+                    TaskStatus = TaskStatusEnum.New.GetHashCode(),
+                    Grade = 1,
+                    TaskNum = await BaseDal.GetTaskNo(),
+                    Creater = "system",
+                };
+
+                return await _unitOfWorkManage.BeginTranAsync(async () =>
+                {
+                    var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationId);
+                    locationInfo.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode();
+                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(locationInfo);
+                    if (!updateLocationResult)
+                        return WebResponseContent.Instance.Error("浠诲姟鍒涘缓澶辫触");
+
+                    var taskDtos = task.Adapt<WMSTaskDTO>();
+
+                    var addResult = await BaseDal.AddDataAsync(task) > 0;
+                    if (!addResult)
+                        return WebResponseContent.Instance.Error("浠诲姟鍒涘缓澶辫触");
+                    return WebResponseContent.Instance.OK("浠诲姟鍒涘缓鎴愬姛", taskDtos);
+                });
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error($"鏌ヨ浠诲姟澶辫触: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 绌烘墭鐩樺嚭搴撳畬鎴�
+        /// </summary>
+        public async Task<WebResponseContent> OutboundFinishTaskTrayAsync(CreateTaskDto taskDto)
+        {
+            try
+            {
+                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
+                if (task == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑浠诲姟");
+
+                var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress);
+                if (location == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑璐т綅");
+
+                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
+                if (stockInfo == null) return WebResponseContent.Instance.Error("鏈壘鍒板搴斿簱瀛樹俊鎭�");
+
+                return await _unitOfWorkManage.BeginTranAsync(async () =>
+                {
+                    stockInfo.LocationId = 0;
+                    stockInfo.LocationCode = string.Empty;
+                    stockInfo.StockStatus = StockStatusEmun.鍑哄簱瀹屾垚.GetHashCode();
+
+                    location.LocationStatus = LocationStatusEnum.Free.GetHashCode();
+
+                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
+                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
+                    if (!updateLocationResult || !updateStockResult)
+                        return WebResponseContent.Instance.Error("浠诲姟瀹屾垚澶辫触");
+
+                    var saveTaskHistoryResult = await SaveTaskHistoryAsync(task, "绌烘墭鐩樺嚭搴撳畬鎴�");
+                    if (!saveTaskHistoryResult.Status)
+                        return saveTaskHistoryResult;
+
+                    var saveStockHistoryResult = await SaveStockHistoryAsync(stockInfo, "绌烘墭鐩樺嚭搴撳畬鎴�");
+                    if (!saveStockHistoryResult.Status)
+                        return saveStockHistoryResult;
+
+                    var deleteResult = await BaseDal.DeleteDataAsync(task);
+                    if (!deleteResult) return WebResponseContent.Instance.Error("浠诲姟瀹屾垚澶辫触");
+
+                    return WebResponseContent.Instance.OK("浠诲姟瀹屾垚");
+                });
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error($"瀹屾垚浠诲姟澶辫触: {ex.Message}");
+            }
+        }
+
+        #endregion 绌烘墭鐩樹换鍔�
+    }
+}
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs
index 571d511..ac0da61 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs
@@ -58,6 +58,17 @@
         }
 
         /// <summary>
+        /// 鎵嬪姩涓嬪彂浠诲姟鍒癢CS
+        /// </summary>
+        /// <param name="dtos">涓嬪彂浠诲姟鍙傛暟鍒楄〃</param>
+        /// <returns>鎵归噺涓嬪彂缁撴灉</returns>
+        [HttpGet, HttpPost, Route("DispatchTasksToWCS"), AllowAnonymous]
+        public async Task<WebResponseContent?> DispatchTasksToWCSAsync([FromBody] List<DispatchTaskDto> dtos)
+        {
+            return await Service.DispatchTasksToWCSAsync(dtos);
+        }
+
+        /// <summary>
         /// 鑾峰彇鍙叆搴撹揣浣�
         /// </summary>
         /// <param name="taskDto"></param>
diff --git a/Code/docs/superpowers/plans/2026-04-12-manual-dispatch-tasks-to-wcs-plan.md b/Code/docs/superpowers/plans/2026-04-12-manual-dispatch-tasks-to-wcs-plan.md
new file mode 100644
index 0000000..2f0f3f7
--- /dev/null
+++ b/Code/docs/superpowers/plans/2026-04-12-manual-dispatch-tasks-to-wcs-plan.md
@@ -0,0 +1,723 @@
+# 鎵嬪姩涓嬪彂浠诲姟鍒� WCS 鍔熻兘瀹炴柦璁″垝
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** 鍦� WMS 鍓嶇浠诲姟绠$悊椤甸潰娣诲姞宸ュ叿鏍忔寜閽紝鏀寔閫変腑涓�涓垨澶氫釜浠诲姟鍚庯紝缂栬緫鍦板潃锛堣捣鐐�/缁堢偣锛夊拰浼樺厛绾у悗鎵嬪姩涓嬪彂鍒� WCS銆�
+
+**Architecture:**
+- 鍓嶇锛氭柊澧� `dispatchTasksToWCS.vue` 寮圭獥缁勪欢锛屽鐢� `task.js` 鎵╁睍鏈哄埗娣诲姞宸ュ叿鏍忔寜閽�
+- 鍚庣锛氭柊澧� `DispatchTasksToWCSAsync` 鏂规硶锛屽鐢� `ReceiveManualTask` 鎺ュ彛涓嬪彂鍒� WCS
+
+**Tech Stack:** Vue 3 + Element Plus锛堝墠绔級锛孉SP.NET Core + SqlSugar锛堝悗绔級
+
+---
+
+## 鏂囦欢缁撴瀯
+
+| 鏀瑰姩绫诲瀷 | 鏂囦欢璺緞 |
+|----------|----------|
+| 鏂板 | `WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/dispatchTasksToWCS.vue` |
+| 淇敼 | `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js` |
+| 鏂板 | `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs` |
+| 淇敼 | `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs` |
+| 淇敼 | `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs` |
+
+---
+
+## Task 1: 鍚庣 - 鏂板 DispatchTaskDto
+
+**鏂囦欢:**
+- 鍒涘缓: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs`
+
+- [ ] **鍒涘缓 DispatchTaskDto.cs**
+
+```csharp
+using System.Text.Json.Serialization;
+
+namespace WIDESEA_DTO.Task
+{
+    /// <summary>
+    /// 鎵嬪姩涓嬪彂浠诲姟Dto
+    /// </summary>
+    public class DispatchTaskDto
+    {
+        /// <summary>
+        /// 浠诲姟ID
+        /// </summary>
+        [JsonPropertyName("taskId")]
+        public long TaskId { get; set; }
+
+        /// <summary>
+        /// 璧风偣鍦板潃
+        /// </summary>
+        [JsonPropertyName("sourceAddress")]
+        public string SourceAddress { get; set; }
+
+        /// <summary>
+        /// 缁堢偣鍦板潃
+        /// </summary>
+        [JsonPropertyName("targetAddress")]
+        public string TargetAddress { get; set; }
+
+        /// <summary>
+        /// 浼樺厛绾�
+        /// </summary>
+        [JsonPropertyName("grade")]
+        public int Grade { get; set; }
+    }
+
+    /// <summary>
+    /// 浠诲姟涓嬪彂缁撴灉Dto
+    /// </summary>
+    public class DispatchTaskResultDto
+    {
+        /// <summary>
+        /// 浠诲姟ID
+        /// </summary>
+        [JsonPropertyName("taskId")]
+        public long TaskId { get; set; }
+
+        /// <summary>
+        /// 浠诲姟鍙�
+        /// </summary>
+        [JsonPropertyName("taskNum")]
+        public int TaskNum { get; set; }
+
+        /// <summary>
+        /// 鏄惁鎴愬姛
+        /// </summary>
+        [JsonPropertyName("success")]
+        public bool Success { get; set; }
+
+        /// <summary>
+        /// 閿欒淇℃伅
+        /// </summary>
+        [JsonPropertyName("errorMessage")]
+        public string ErrorMessage { get; set; }
+    }
+
+    /// <summary>
+    /// 鎵归噺涓嬪彂缁撴灉Dto
+    /// </summary>
+    public class DispatchResultDto
+    {
+        /// <summary>
+        /// 鎴愬姛鏁伴噺
+        /// </summary>
+        [JsonPropertyName("successCount")]
+        public int SuccessCount { get; set; }
+
+        /// <summary>
+        /// 澶辫触鏁伴噺
+        /// </summary>
+        [JsonPropertyName("failCount")]
+        public int FailCount { get; set; }
+
+        /// <summary>
+        /// 澶辫触浠诲姟鍒楄〃
+        /// </summary>
+        [JsonPropertyName("failResults")]
+        public List<DispatchTaskResultDto> FailResults { get; set; }
+    }
+}
+```
+
+- [ ] **Commit**
+
+```bash
+git add WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs
+git commit -m "feat(WMS): 鏂板鎵嬪姩涓嬪彂浠诲姟Dto
+- DispatchTaskDto: 涓嬪彂璇锋眰鍙傛暟
+- DispatchTaskResultDto: 鍗曚釜浠诲姟涓嬪彂缁撴灉
+- DispatchResultDto: 鎵归噺涓嬪彂缁撴灉
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 2: 鍚庣 - TaskService 鏂板 DispatchTasksToWCSAsync
+
+**鏂囦欢:**
+- 淇敼: `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs`
+
+**鍓嶇疆浜嗚В:**
+- 浠诲姟鐘舵�佹灇涓撅細`TaskInStatusEnum.InNew`锛堝叆搴撴柊鍗曪級銆乣TaskOutStatusEnum.OutNew`锛堝嚭搴撴柊鍗曪級銆乣TaskRelocationStatusEnum.RelocationNew`锛堢Щ搴撴柊鍗曪級
+- 宸叉湁 WCS 璋冪敤鏂瑰紡锛歚_httpClientHelper.Post<WebResponseContent>("http://localhost:9292/api/Task/ReceiveManualTask", json)`
+- 宸叉湁浜嬪姟澶勭悊锛歚await _unitOfWorkManage.BeginTranAsync(async () => { ... })`
+- 宸叉湁 `WMSTaskDTO` 鐢ㄤ簬缁� WCS 鍙戦�佷换鍔�
+
+**鍦� `TaskService_WCS.cs` 鏈熬 `#endregion WCS閫昏緫澶勭悊` 涔嬪墠娣诲姞锛�**
+
+```csharp
+/// <summary>
+/// 鎵嬪姩涓嬪彂浠诲姟鍒癢CS
+/// </summary>
+/// <param name="dtos">涓嬪彂浠诲姟鍙傛暟鍒楄〃</param>
+/// <returns>鎵归噺涓嬪彂缁撴灉</returns>
+public async Task<WebResponseContent> DispatchTasksToWCSAsync(List<DispatchTaskDto> dtos)
+{
+    try
+    {
+        if (dtos == null || !dtos.Any())
+            return WebResponseContent.Instance.Error("璇烽�夋嫨瑕佷笅鍙戠殑浠诲姟");
+
+        var resultDto = new DispatchResultDto
+        {
+            SuccessCount = 0,
+            FailCount = 0,
+            FailResults = new List<DispatchTaskResultDto>()
+        };
+
+        foreach (var dto in dtos)
+        {
+            var task = await BaseDal.QueryFirstAsync(t => t.TaskId == dto.TaskId);
+            if (task == null)
+            {
+                resultDto.FailResults.Add(new DispatchTaskResultDto
+                {
+                    TaskId = dto.TaskId,
+                    TaskNum = 0,
+                    Success = false,
+                    ErrorMessage = "浠诲姟涓嶅瓨鍦�"
+                });
+                resultDto.FailCount++;
+                continue;
+            }
+
+            // 鏍¢獙浠诲姟鐘舵�侊細浠呭叆搴撴柊鍗�/鍑哄簱鏂板崟/绉诲簱鏂板崟鍙笅鍙�
+            bool canDispatch = false;
+            if (task.TaskType == TaskInboundTypeEnum.Inbound.GetHashCode()
+                && task.TaskStatus == TaskInStatusEnum.InNew.GetHashCode())
+                canDispatch = true;
+            else if (task.TaskType == TaskOutboundTypeEnum.Outbound.GetHashCode()
+                && task.TaskStatus == TaskOutStatusEnum.OutNew.GetHashCode())
+                canDispatch = true;
+            else if (task.TaskType == TaskRelocationTypeEnum.Relocation.GetHashCode()
+                && task.TaskStatus == TaskRelocationStatusEnum.RelocationNew.GetHashCode())
+                canDispatch = true;
+
+            if (!canDispatch)
+            {
+                var statusName = GetTaskStatusName(task.TaskType, task.TaskStatus);
+                resultDto.FailResults.Add(new DispatchTaskResultDto
+                {
+                    TaskId = dto.TaskId,
+                    TaskNum = task.TaskNum,
+                    Success = false,
+                    ErrorMessage = $"浠诲姟鐘舵�乕{statusName}]涓嶅厑璁镐笅鍙�"
+                });
+                resultDto.FailCount++;
+                continue;
+            }
+
+            // 鏇存柊浠诲姟鐨勫湴鍧�鍜屼紭鍏堢骇
+            task.SourceAddress = dto.SourceAddress;
+            task.TargetAddress = dto.TargetAddress;
+            task.CurrentAddress = dto.SourceAddress;
+            task.NextAddress = dto.TargetAddress;
+            task.Grade = dto.Grade;
+            task.Dispatchertime = DateTime.Now;
+
+            await BaseDal.UpdateDataAsync(task);
+
+            // 鏋勯�燱MSTaskDTO鍙戦�佺粰WCS
+            var wmsTaskDto = new WMSTaskDTO
+            {
+                Id = task.TaskId,
+                TaskNum = task.TaskNum,
+                PalletCode = task.PalletCode,
+                SourceAddress = task.SourceAddress,
+                TargetAddress = task.TargetAddress,
+                CurrentAddress = task.CurrentAddress,
+                NextAddress = task.NextAddress,
+                TaskType = task.TaskType,
+                TaskStatus = task.TaskStatus,
+                Roadway = task.Roadway,
+                Grade = task.Grade,
+                WarehouseId = task.WarehouseId,
+                PalletType = task.PalletType
+            };
+
+            var wcsResult = _httpClientHelper.Post<WebResponseContent>(
+                "http://localhost:9292/api/Task/ReceiveManualTask",
+                new List<WMSTaskDTO> { wmsTaskDto }.ToJson());
+
+            if (wcsResult != null && wcsResult.IsSuccess)
+            {
+                resultDto.SuccessCount++;
+            }
+            else
+            {
+                resultDto.FailResults.Add(new DispatchTaskResultDto
+                {
+                    TaskId = dto.TaskId,
+                    TaskNum = task.TaskNum,
+                    Success = false,
+                    ErrorMessage = wcsResult?.Message ?? "WCS鍝嶅簲寮傚父"
+                });
+                resultDto.FailCount++;
+            }
+        }
+
+        if (resultDto.FailCount == 0)
+            return WebResponseContent.Instance.OK($"鎴愬姛涓嬪彂{resultDto.SuccessCount}涓换鍔�", resultDto);
+
+        if (resultDto.SuccessCount == 0)
+            return WebResponseContent.Instance.Error($"涓嬪彂澶辫触锛屽叡{resultDto.FailCount}涓换鍔�", resultDto);
+
+        return WebResponseContent.Instance.Error($"閮ㄥ垎涓嬪彂鎴愬姛{resultDto.SuccessCount}涓紝澶辫触{resultDto.FailCount}涓�", resultDto);
+    }
+    catch (Exception ex)
+    {
+        return WebResponseContent.Instance.Error($"涓嬪彂浠诲姟寮傚父: {ex.Message}");
+    }
+}
+
+/// <summary>
+/// 鑾峰彇浠诲姟鐘舵�佸悕绉�
+/// </summary>
+private string GetTaskStatusName(int taskType, int taskStatus)
+{
+    if (taskType == TaskInboundTypeEnum.Inbound.GetHashCode())
+        return ((TaskInStatusEnum)taskStatus).ToString();
+    if (taskType == TaskOutboundTypeEnum.Outbound.GetHashCode())
+        return ((TaskOutStatusEnum)taskStatus).ToString();
+    if (taskType == TaskRelocationTypeEnum.Relocation.GetHashCode())
+        return ((TaskRelocationStatusEnum)taskStatus).ToString();
+    return taskStatus.ToString();
+}
+```
+
+**娉ㄦ剰锛�** 闇�瑕佸湪鏂囦欢椤堕儴纭宸叉湁浠ヤ笅 using锛�
+```csharp
+using WIDESEA_DTO.Task;
+
+> **鐘舵�佸悓姝ヨ鏄�**锛歐MS 涓嬪彂鎴愬姛鍚庯紝WMS 绔换鍔$姸鎬佷繚鎸�"New"涓嶅彉锛屼笉鍒囨崲鍒版墽琛屼腑鐘舵�併�傝繖鏄幇鏈� `CreateManualTaskAsync` 鐨勮璁℃ā寮忥紙鍥犱负 WCS 渚ф湁鐙珛鐨勪换鍔$敓鍛藉懆鏈燂級锛屼笉璺熻繘姝� PR 鏀瑰彉銆傚闇� WMS 绔篃鍚屾鐘舵�侊紝闇�鍙﹁璇勪及 WCS鈫扺MS 鐨勭姸鎬佸洖璋冩満鍒躲��
+using WIDESEA_Model.Models;
+```
+
+- [ ] **Commit**
+
+```bash
+git add WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs
+git commit -m "feat(WMS): TaskService鏂板DispatchTasksToWCSAsync鏂规硶
+- 鏍¢獙浠诲姟鐘舵�侊紝浠呭叆搴撴柊鍗�/鍑哄簱鏂板崟/绉诲簱鏂板崟鍙笅鍙�
+- 鏇存柊浠诲姟鍦板潃鍜屼紭鍏堢骇鍚庤皟鐢╓CS ReceiveManualTask鎺ュ彛
+- 杩斿洖鎵归噺涓嬪彂缁撴灉锛堟垚鍔�/澶辫触鍒楄〃鍙婂師鍥狅級
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 3: 鍚庣 - TaskController 鏂板涓嬪彂鎺ュ彛
+
+**鏂囦欢:**
+- 淇敼: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs`
+
+**鍦� Controller 涓坊鍔犱互涓嬫柟娉曪紙鏀惧湪鍏朵粬鏂规硶闄勮繎锛屽缓璁湪 CreateManualTaskAsync 涔嬪悗锛夛細**
+
+```csharp
+/// <summary>
+/// 鎵嬪姩涓嬪彂浠诲姟鍒癢CS
+/// </summary>
+/// <param name="dtos">涓嬪彂浠诲姟鍙傛暟鍒楄〃</param>
+/// <returns>鎵归噺涓嬪彂缁撴灉</returns>
+[HttpGet, HttpPost, Route("DispatchTasksToWCS"), AllowAnonymous]
+public async Task<WebResponseContent?> DispatchTasksToWCSAsync([FromBody] List<DispatchTaskDto> dtos)
+{
+    return await Service.DispatchTasksToWCSAsync(dtos);
+}
+```
+
+- [ ] **Commit**
+
+```bash
+git add WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs
+git commit -m "feat(WMS): TaskController鏂板DispatchTasksToWCS鎺ュ彛
+POST /api/TaskInfo/DispatchTasksToWCS
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 4: 鍓嶇 - 鏂板 dispatchTasksToWCS.vue 寮圭獥缁勪欢
+
+**鏂囦欢:**
+- 鍒涘缓: `WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/dispatchTasksToWCS.vue`
+
+**鍙傝�冪幇鏈� addManualTask.vue 鐨勬ā寮忥紝浣跨敤 el-table 瀹炵幇鎵归噺缂栬緫锛�**
+
+```vue
+<template>
+  <div>
+    <vol-box v-model="showBox" :lazy="true" width="900px" :padding="15" title="鎵嬪姩涓嬪彂浠诲姟鍒� WCS">
+      <div v-if="selectedRows.length > 0" class="dispatch-info">
+        宸查�変换鍔℃暟: <span class="count">{{ selectedRows.length }}</span> 涓�
+      </div>
+
+      <el-table :data="tableData" border style="width: 100%; margin-top: 10px" max-height="400">
+        <el-table-column prop="taskNum" label="浠诲姟鍙�" width="120"></el-table-column>
+        <el-table-column prop="sourceAddress" label="璧风偣鍦板潃" width="160">
+          <template slot-scope="scope">
+            <el-input
+              v-if="scope.row.editable"
+              v-model="scope.row.sourceAddress"
+              size="small"
+              placeholder="璇疯緭鍏�"
+            ></el-input>
+            <span v-else>{{ scope.row.sourceAddress }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="targetAddress" label="缁堢偣鍦板潃" width="160">
+          <template slot-scope="scope">
+            <el-input
+              v-if="scope.row.editable"
+              v-model="scope.row.targetAddress"
+              size="small"
+              placeholder="璇疯緭鍏�"
+            ></el-input>
+            <span v-else>{{ scope.row.targetAddress }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="grade" label="浼樺厛绾�" width="100">
+          <template slot-scope="scope">
+            <el-input-number
+              v-if="scope.row.editable"
+              v-model="scope.row.grade"
+              :min="1"
+              :max="99"
+              size="small"
+              style="width: 80px"
+            ></el-input-number>
+            <span v-else>{{ scope.row.grade }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="statusName" label="鐘舵��" width="120">
+          <template slot-scope="scope">
+            <span :class="{ 'status-error': !scope.row.editable }">{{ scope.row.statusName }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="palletCode" label="鎵樼洏鍙�"></el-table-column>
+      </el-table>
+
+      <!-- 澶辫触缁撴灉 -->
+      <div v-if="failResults.length > 0" class="fail-results">
+        <div class="fail-title">涓嬪彂澶辫触浠诲姟锛�</div>
+        <el-table :data="failResults" border style="width: 100%; margin-top: 10px" max-height="200">
+          <el-table-column prop="taskNum" label="浠诲姟鍙�" width="120"></el-table-column>
+          <el-table-column prop="errorMessage" label="澶辫触鍘熷洜"></el-table-column>
+        </el-table>
+      </div>
+
+      <template #footer>
+        <el-button type="primary" size="small" @click="submit" :loading="loading">纭涓嬪彂</el-button>
+        <el-button type="danger" size="small" @click="showBox = false">鍏抽棴</el-button>
+      </template>
+    </vol-box>
+  </div>
+</template>
+
+<script>
+import VolBox from "@/components/basic/VolBox.vue";
+
+export default {
+  components: { VolBox },
+  data() {
+    return {
+      showBox: false,
+      loading: false,
+      selectedRows: [],
+      tableData: [],
+      failResults: [],
+      // 鍙笅鍙戠姸鎬佺殑浠诲姟绫诲瀷鍜岀姸鎬佸�硷紙闇�涓庡悗绔� TaskTypeEnum 鏋氫妇涓�鑷达級
+      // Inbound=200, Outbound=100, Relocation=300
+      // 鐘舵��: InNew=200, OutNew=100, RelocationNew=300
+      dispatchableStatuses: {
+        inbound: { taskType: 200, status: 200 },    // 鍏ュ簱鏂板崟
+        outbound: { taskType: 100, status: 100 },  // 鍑哄簱鏂板崟
+        relocation: { taskType: 300, status: 300 } // 绉诲簱鏂板崟
+      }
+    };
+  },
+  methods: {
+    open(rows) {
+      this.showBox = true;
+      this.selectedRows = rows || [];
+      this.failResults = [];
+      this.initTableData();
+    },
+    initTableData() {
+      // 浠诲姟鐘舵�佸悕绉版槧灏勶紙闇�涓庡悗绔灇涓句竴鑷达級
+      // taskType: Inbound=200, Outbound=100, Relocation=300
+      // status: InNew=200, OutNew=100, RelocationNew=300, SC_OutExecuting=110, etc.
+      const statusNames = {
+        'inbound_200': '鍏ュ簱鏂板崟',
+        'outbound_100': '鍑哄簱鏂板崟',
+        'relocation_300': '绉诲簱鏂板崟',
+        'outbound_110': '鍫嗗灈鏈哄嚭搴撴墽琛屼腑',
+        'outbound_115': '鍫嗗灈鏈哄嚭搴撳畬鎴�',
+        'inbound_220': '杈撻�佺嚎鍏ュ簱鎵ц涓�',
+        'inbound_230': '鍫嗗灈鏈哄叆搴撴墽琛屼腑',
+        'inbound_290': '鍏ュ簱浠诲姟瀹屾垚',
+        'outbound_120': '杈撻�佺嚎鍑哄簱鎵ц涓�',
+        'outbound_125': '杈撻�佺嚎鍑哄簱瀹屾垚',
+        'outbound_200': '鍑哄簱浠诲姟瀹屾垚',
+        'relocation_310': '鍫嗗灈鏈虹Щ搴撴墽琛屼腑'
+      };
+
+      this.tableData = this.selectedRows.map(row => {
+        const taskType = row.taskType || 0;
+        const taskStatus = row.taskStatus || 0;
+        const statusKey = this.getStatusKey(taskType, taskStatus);
+        const statusName = statusNames[statusKey] || `鐘舵��${taskStatus}`;
+
+        // 鍒ゆ柇鏄惁鍙紪杈戯細浠呭叆搴撴柊鍗�/鍑哄簱鏂板崟/绉诲簱鏂板崟鍙紪杈�
+        const editable = this.isEditable(taskType, taskStatus);
+
+        return {
+          taskId: row.taskId,
+          taskNum: row.taskNum,
+          sourceAddress: row.sourceAddress || '',
+          targetAddress: row.targetAddress || '',
+          grade: row.grade || 1,
+          statusName: statusName,
+          palletCode: row.palletCode || '',
+          editable: editable,
+          taskType: taskType,
+          taskStatus: taskStatus
+        };
+      });
+    },
+    getStatusKey(taskType, taskStatus) {
+      if (taskType === 200) return `inbound_${taskStatus}`;
+      if (taskType === 100) return `outbound_${taskStatus}`;
+      if (taskType === 300) return `relocation_${taskStatus}`;
+      return `other_${taskStatus}`;
+    },
+    isEditable(taskType, taskStatus) {
+      if (taskType === 200 && taskStatus === 200) return true;  // 鍏ュ簱鏂板崟
+      if (taskType === 100 && taskStatus === 100) return true;  // 鍑哄簱鏂板崟
+      if (taskType === 300 && taskStatus === 300) return true;  // 绉诲簱鏂板崟
+      return false;
+    },
+    submit() {
+      if (this.tableData.length === 0) return this.$message.error("璇峰厛閫夋嫨浠诲姟");
+
+      const dispatchData = this.tableData.map(row => ({
+        taskId: row.taskId,
+        sourceAddress: row.sourceAddress,
+        targetAddress: row.targetAddress,
+        grade: row.grade
+      }));
+
+      this.loading = true;
+      this.http
+        .post("/api/TaskInfo/DispatchTasksToWCS", dispatchData, "鏁版嵁澶勭悊涓�...")
+        .then((res) => {
+          this.loading = false;
+          if (!res.status) {
+            this.$message.error(res.message);
+            // 鏄剧ず澶辫触鍒楄〃
+            if (res.data && res.data.failResults) {
+              this.failResults = res.data.failResults;
+            }
+            return;
+          }
+
+          // 鍏ㄩ儴鎴愬姛
+          if (res.data && res.data.failCount === 0) {
+            this.$message.success(res.message);
+            this.showBox = false;
+            this.$emit("parentCall", ($vue) => {
+              $vue.refresh();
+            });
+            return;
+          }
+
+          // 閮ㄥ垎鎴愬姛鎴栧叏閮ㄥけ璐�
+          if (res.data && res.data.failResults) {
+            this.failResults = res.data.failResults;
+          }
+
+          if (res.data && res.data.failCount > 0 && res.data.successCount > 0) {
+            this.$message.warning(res.message);
+          } else {
+            this.$message.error(res.message);
+          }
+        })
+        .catch(() => {
+          this.loading = false;
+        });
+    }
+  }
+};
+</script>
+
+<style scoped>
+.dispatch-info {
+  font-size: 14px;
+  color: #606266;
+  margin-bottom: 10px;
+}
+.dispatch-info .count {
+  color: #409eff;
+  font-weight: bold;
+}
+.status-error {
+  color: #f56c6c;
+}
+.fail-results {
+  margin-top: 15px;
+  padding: 10px;
+  background: #fef0f0;
+  border-radius: 4px;
+}
+.fail-title {
+  font-size: 14px;
+  color: #f56c6c;
+  margin-bottom: 5px;
+}
+</style>
+```
+
+- [ ] **Commit**
+
+```bash
+git add WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/dispatchTasksToWCS.vue
+git commit -m "feat(WMS): 鏂板dispatchTasksToWCS.vue鎵归噺涓嬪彂寮圭獥缁勪欢
+- 宸ュ叿鏍忔寜閽Е鍙戝脊绐�
+- 琛ㄦ牸灞曠ず閫変腑浠诲姟锛屽彲缂栬緫鍦板潃鍜屼紭鍏堢骇
+- 闈炲彲涓嬪彂鐘舵�佷换鍔¤鏍囩孩涓斾笉鍙紪杈�
+- 鏄剧ず涓嬪彂澶辫触浠诲姟鍒楄〃
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 5: 鍓嶇 - task.js 娣诲姞宸ュ叿鏍忔寜閽�
+
+**鏂囦欢:**
+- 淇敼: `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js`
+
+**闇�瑕佷慨鏀逛袱澶勶細**
+
+1. 鍦� `components` 閮ㄥ垎寮曞叆寮圭獥缁勪欢锛�
+```javascript
+import addManualTask from './extend/addManualTask.vue'
+import dispatchTasksToWCS from './extend/dispatchTasksToWCS.vue'  // 鏂板
+```
+
+2. 鍦� `components` 瀵硅薄涓敞鍐岋細
+```javascript
+components: {
+  gridBody: addManualTask,
+  dispatchBody: dispatchTasksToWCS,  // 鏂板
+},
+```
+
+3. 鍦� `onInit()` 鏂规硶涓坊鍔犳墜鍔ㄤ笅鍙戞寜閽紙鍦ㄦ墜鍔ㄥ垱寤轰换鍔℃寜閽箣鍚庯級锛�
+
+```javascript
+// 娣诲姞"鎵嬪姩涓嬪彂"鎸夐挳
+this.buttons.push({
+  name: '鎵嬪姩涓嬪彂',
+  icon: 'el-icon-s-promotion',
+  type: 'primary',
+  value: 'DispatchTasksToWCS',
+  onClick: () => {
+    let rows = this.$refs.table.getSelected();
+    if (rows.length == 0) return this.$error("璇峰厛閫夋嫨浠诲姟");
+    this.$refs.dispatchBody.open(rows);
+  }
+});
+```
+
+**瀹屾暣淇敼鍚庣殑 task.js 鍏抽敭閮ㄥ垎锛�**
+
+```javascript
+import addManualTask from './extend/addManualTask.vue'
+import dispatchTasksToWCS from './extend/dispatchTasksToWCS.vue'
+
+let extension = {
+    components: {
+      gridBody: addManualTask,
+      dispatchBody: dispatchTasksToWCS,
+    },
+    buttons: { view: [], box: [], detail: [] },
+    methods: {
+      onInit() {
+        //娣诲姞"鎵嬪姩鍒涘缓浠诲姟"鎸夐挳
+        this.buttons.push({
+          name: '鎵嬪姩鍒涘缓浠诲姟',
+          icon: 'el-icon-plus',
+          type: 'primary',
+          value: 'ManualCreateTask',
+          onClick: () => {
+            this.$refs.gridBody.open();
+          }
+        });
+
+        // 娣诲姞"鎵嬪姩涓嬪彂"鎸夐挳
+        this.buttons.push({
+          name: '鎵嬪姩涓嬪彂',
+          icon: 'el-icon-s-promotion',
+          type: 'primary',
+          value: 'DispatchTasksToWCS',
+          onClick: () => {
+            let rows = this.$refs.table.getSelected();
+            if (rows.length == 0) return this.$error("璇峰厛閫夋嫨浠诲姟");
+            this.$refs.dispatchBody.open(rows);
+          }
+        });
+        // ... 鍏朵綑鐜版湁鎸夐挳閫昏緫淇濇寔涓嶅彉
+      },
+      // ... 鍏朵綑鏂规硶淇濇寔涓嶅彉
+    }
+};
+export default extension;
+```
+
+- [ ] **Commit**
+
+```bash
+git add WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js
+git commit -m "feat(WMS): task.js娣诲姞宸ュ叿鏍�'鎵嬪姩涓嬪彂'鎸夐挳
+- 寮曞叆dispatchTasksToWCS.vue缁勪欢
+- 鐐瑰嚮鎸夐挳鑾峰彇閫変腑浠诲姟骞舵墦寮�涓嬪彂寮圭獥
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 6: 楠岃瘉鏋勫缓
+
+- [ ] **WMS 鍚庣鏋勫缓**
+
+```bash
+cd D:/Git/ShanMeiXinNengYuan/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer
+dotnet build
+```
+
+- [ ] **WMS 鍓嶇鏋勫缓**
+
+```bash
+cd D:/Git/ShanMeiXinNengYuan/Code/WMS/WIDESEA_WMSClient
+npm run serve
+```
+
+---
+
+## 楠屾敹鏍囧噯
+
+- [ ] 宸ュ叿鏍忔坊鍔�"鎵嬪姩涓嬪彂"鎸夐挳锛屾湭閫変腑浠诲姟鏃剁偣鍑绘彁绀�"璇峰厛閫夋嫨浠诲姟"
+- [ ] 寮圭獥姝g‘鏄剧ず鎵�鏈夐�変腑浠诲姟
+- [ ] 浠呭叆搴撴柊鍗�/鍑哄簱鏂板崟/绉诲簱鏂板崟鐘舵�佺殑浠诲姟鍙紪杈戝湴鍧�鍜屼紭鍏堢骇
+- [ ] 鍏朵粬鐘舵�佷换鍔¤鏍囩孩锛屽湴鍧�/浼樺厛绾т笉鍙紪杈�
+- [ ] 纭涓嬪彂鍚庤皟鐢ㄥ悗绔帴鍙�
+- [ ] 鎴愬姛鏃跺脊绐楀叧闂苟鍒锋柊鍒楄〃锛涘け璐ユ椂鏄剧ず澶辫触浠诲姟鍙婂師鍥�
+- [ ] 鎵归噺涓嬪彂鏃舵纭粺璁℃垚鍔�/澶辫触鏁伴噺
diff --git a/Code/docs/superpowers/specs/2026-04-12-manual-dispatch-tasks-to-wcs-design.md b/Code/docs/superpowers/specs/2026-04-12-manual-dispatch-tasks-to-wcs-design.md
new file mode 100644
index 0000000..8ce129f
--- /dev/null
+++ b/Code/docs/superpowers/specs/2026-04-12-manual-dispatch-tasks-to-wcs-design.md
@@ -0,0 +1,154 @@
+# 鎵嬪姩涓嬪彂浠诲姟鍒� WCS 鍔熻兘璁捐
+
+**鏃ユ湡**: 2026-04-12
+**鐘舵��**: 宸叉壒鍑�
+
+---
+
+## 1. 鍔熻兘姒傝堪
+
+鍦� WMS 鍓嶇浠诲姟绠$悊椤甸潰娣诲姞宸ュ叿鏍忔寜閽紝鏀寔浠庝换鍔″垪琛ㄤ腑閫夋嫨涓�涓垨澶氫釜浠诲姟锛屾墜鍔ㄧ紪杈戝湴鍧�锛堣捣鐐�/缁堢偣锛夊拰浼樺厛绾у悗锛屼笅鍙戝埌 WCS 绯荤粺鎵ц銆�
+
+**浣跨敤鍦烘櫙**锛氬綋浠诲姟鍥犺澶囨晠闅溿�佺綉缁滈棶棰樼瓑鍘熷洜鏈兘姝e父涓嬪彂鏃讹紝鎿嶄綔鍛樺彲鎵嬪姩閲嶆柊涓嬪彂銆�
+
+---
+
+## 2. 鍓嶇鏀瑰姩
+
+### 2.1 鏂板鏂囦欢
+
+| 鏂囦欢 | 鐢ㄩ�� |
+|------|------|
+| `WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/dispatchTasksToWCS.vue` | 鎵归噺涓嬪彂缂栬緫寮圭獥缁勪欢 |
+
+### 2.2 淇敼鏂囦欢
+
+| 鏂囦欢 | 鏀瑰姩 |
+|------|------|
+| `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js` | 娣诲姞宸ュ叿鏍�"鎵嬪姩涓嬪彂"鎸夐挳 |
+
+### 2.3 寮圭獥浜や簰
+
+**瑙﹀彂鏂瑰紡**锛氬伐鍏锋爮鐐瑰嚮"鎵嬪姩涓嬪彂"鎸夐挳
+
+**鍓嶇疆鏉′欢**锛氳嚦灏戦�変腑涓�椤逛换鍔�
+
+**寮圭獥鍐呭**锛�
+- 椤堕儴鏄剧ず"宸查�変换鍔℃暟: N 涓�"
+- 琛ㄦ牸灞曠ず鎵�鏈夐�変腑浠诲姟锛屽垪锛氫换鍔″彿銆佽捣鐐瑰湴鍧�銆佺粓鐐瑰湴鍧�銆佷紭鍏堢骇銆佺姸鎬�
+- 鍦板潃瀛楁锛歚el-input` 鎴� `el-select`锛堝彇鍐充簬鏄惁鏈夋爣鍑嗗湴鍧�瀛楀吀锛�
+- 浼樺厛绾э細`el-input-number`锛岃寖鍥� 1-99
+- 鐘舵�佸垪锛氫粎灞曠ず锛屼笉鍙紪杈�
+
+**鎸夐挳**锛氬彇娑堛�佺‘璁や笅鍙�
+
+**涓嬪彂鍚�**锛�
+- 鎴愬姛锛氬脊绐楀叧闂紝鍒锋柊浠诲姟鍒楄〃
+- 澶辫触锛氬脊绐楀唴鏄剧ず澶辫触浠诲姟鍒楄〃锛堜换鍔″彿 + 澶辫触鍘熷洜锛�
+
+---
+
+## 3. 鍚庣鏀瑰姩
+
+### 3.1 鏂板 DTO
+
+**鏂囦欢**: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs`
+
+```csharp
+public class DispatchTaskDto
+{
+    public long TaskId { get; set; }
+    public string SourceAddress { get; set; }
+    public string TargetAddress { get; set; }
+    public int Priority { get; set; }
+}
+```
+
+### 3.2 鏂板鎺ュ彛
+
+**Controller**: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs`
+
+```
+POST /api/TaskInfo/DispatchTasksToWCS
+```
+
+**Service**: `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs`
+
+鏂板鏂规硶 `DispatchTasksToWCSAsync(List<DispatchTaskDto> dtos)`
+
+### 3.3 涓氬姟閫昏緫
+
+```
+1. 鏍¢獙閫変腑浠诲姟鏁伴噺 > 0
+2. 鏍¢獙浠诲姟鐘舵�侊紝浠呭厑璁镐互涓嬬姸鎬佺殑浠诲姟涓嬪彂锛�
+   - 鍏ュ簱锛欼nNew锛堝叆搴撴柊鍗曪級
+   - 鍑哄簱锛歄utNew锛堝嚭搴撴柊鍗曪級
+   - 绉诲簱锛歊elocationNew锛堢Щ搴撴柊鍗曪級
+3. 鏇存柊浠诲姟鐨勫湴鍧�锛圫ourceAddress銆乀argetAddress锛夊拰浼樺厛绾э紙Grade锛�
+4. 璋冪敤 WCS 鎺ュ彛锛歅OST http://localhost:9292/api/Task/ReceiveManualTask
+5. 姹囨�绘瘡鏉′换鍔$殑涓嬪彂缁撴灉锛堟垚鍔�/澶辫触鍙婂師鍥狅級
+6. 杩斿洖缁欏墠绔�
+```
+
+### 3.4 WCS 鎺ュ彛澶嶇敤
+
+**宸叉湁鎺ュ彛**: `POST /api/Task/ReceiveManualTask`锛坈ommit b032763锛�
+
+WMS 璋冪敤璇ユ帴鍙o紝灏嗕换鍔℃暟鎹互 `WMSTaskDTO` 鏍煎紡鍙戦�併��
+
+---
+
+## 4. 閿欒澶勭悊
+
+| 鍦烘櫙 | 鍓嶇澶勭悊 | 鍚庣澶勭悊 |
+|------|----------|----------|
+| 鏈�変腑浠诲姟 | 鎸夐挳缃伆锛涚偣鍑绘彁绀�"璇峰厛閫夋嫨浠诲姟" | 杩斿洖閿欒鎻愮ず |
+| 浠诲姟鐘舵�佷笉鍙笅鍙� | 琛屽唴鐘舵�佸垪鏍囩孩锛屽湴鍧�/浼樺厛绾х疆鐏颁笉鍙紪杈� | 鏍¢獙骞惰繑鍥炰笉鍙笅鍙戜换鍔″垪琛� |
+| WCS 璋冪敤瓒呮椂 | 璇ヤ换鍔℃爣璁�"涓嬪彂瓒呮椂" | 鏍囪浠诲姟锛屼笅鍙戠粨鏋滆繑鍥炲墠绔� |
+| WCS 杩斿洖澶辫触 | 鏄剧ず WCS 閿欒鍘熷洜锛堝"璁惧蹇�"锛� | 閫忎紶 WCS 閿欒淇℃伅 |
+| 鍏ㄩ儴鎴愬姛 | 寮圭獥鍏抽棴锛屽埛鏂板垪琛� | 杩斿洖鎴愬姛 |
+
+---
+
+## 5. 娑夊強鐨勪唬鐮佹枃浠�
+
+| 灞傜骇 | 鏂囦欢璺緞 | 鏀瑰姩绫诲瀷 |
+|------|----------|----------|
+| 鍓嶇 | `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js` | 淇敼 |
+| 鍓嶇 | `WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/dispatchTasksToWCS.vue` | 鏂板 |
+| 鍓嶇 | `WMS/WIDESEA_WMSClient/src/api/` | 鏂板 API 鏂规硶 |
+| 鍚庣 | `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs` | 鏂板 |
+| 鍚庣 | `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs` | 淇敼 |
+| 鍚庣 | `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs` | 淇敼 |
+| 鍚庣 | `WMS/WIDESEA_WMSServer/WIDESEA_Core/Helper/HTTP/HttpClientHelper.cs` | 澶嶇敤 |
+
+---
+
+## 6. UI 绀烘剰鍥�
+
+```
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹�  鎵嬪姩涓嬪彂浠诲姟鍒� WCS                                    [X]  鈹�
+鈹溾攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹�  宸查�変换鍔℃暟: 5 涓�                                          鈹�
+鈹�  鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�   鈹�
+鈹�  鈹� 浠诲姟鍙� 鈹� 璧风偣鍦板潃     鈹� 缁堢偣鍦板潃    鈹� 浼樺厛绾� 鈹� 鐘舵��  鈹�   鈹�
+鈹�  鈹溾攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�   鈹�
+鈹�  鈹� T001   鈹� [PLC-A01  ] 鈹� [Rack-05] 鈹� [ 5 ] 鈹� 鍏ュ簱鏂板崟鈹�   鈹�
+鈹�  鈹� T002   鈹� [PLC-A02  ] 鈹� [Rack-06] 鈹� [ 3 ] 鈹� 鍏ュ簱鏂板崟鈹�   鈹�
+鈹�  鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�   鈹�
+鈹溾攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹�                                    [鍙栨秷]  [纭涓嬪彂]       鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+```
+
+---
+
+## 7. 楠屾敹鏍囧噯
+
+- [ ] 宸ュ叿鏍忔坊鍔�"鎵嬪姩涓嬪彂"鎸夐挳锛屾湭閫変腑浠诲姟鏃舵寜閽彲鐢ㄤ絾鐐瑰嚮鎻愮ず閫夋嫨
+- [ ] 寮圭獥姝g‘鏄剧ず鎵�鏈夐�変腑浠诲姟
+- [ ] 浠呭叆搴撴柊鍗�/鍑哄簱鏂板崟/绉诲簱鏂板崟鐘舵�佺殑浠诲姟鍙紪杈戝湴鍧�鍜屼紭鍏堢骇
+- [ ] 纭涓嬪彂鍚庤皟鐢ㄥ悗绔帴鍙�
+- [ ] 鎴愬姛/澶辫触缁撴灉姝g‘鍙嶉缁欑敤鎴�
+- [ ] 鎴愬姛鍚庡埛鏂颁换鍔″垪琛�

--
Gitblit v1.9.3