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