# 手动下发任务到 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(前端),ASP.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 { /// /// 手动下发任务Dto /// public class DispatchTaskDto { /// /// 任务ID /// [JsonPropertyName("taskId")] public long TaskId { get; set; } /// /// 起点地址 /// [JsonPropertyName("sourceAddress")] public string SourceAddress { get; set; } /// /// 终点地址 /// [JsonPropertyName("targetAddress")] public string TargetAddress { get; set; } /// /// 优先级 /// [JsonPropertyName("grade")] public int Grade { get; set; } } /// /// 任务下发结果Dto /// public class DispatchTaskResultDto { /// /// 任务ID /// [JsonPropertyName("taskId")] public long TaskId { get; set; } /// /// 任务号 /// [JsonPropertyName("taskNum")] public int TaskNum { get; set; } /// /// 是否成功 /// [JsonPropertyName("success")] public bool Success { get; set; } /// /// 错误信息 /// [JsonPropertyName("errorMessage")] public string ErrorMessage { get; set; } } /// /// 批量下发结果Dto /// public class DispatchResultDto { /// /// 成功数量 /// [JsonPropertyName("successCount")] public int SuccessCount { get; set; } /// /// 失败数量 /// [JsonPropertyName("failCount")] public int FailCount { get; set; } /// /// 失败任务列表 /// [JsonPropertyName("failResults")] public List 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 " ``` --- ## Task 2: 后端 - TaskService 新增 DispatchTasksToWCSAsync **文件:** - 修改: `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs` **前置了解:** - 任务状态枚举:`TaskInStatusEnum.InNew`(入库新单)、`TaskOutStatusEnum.OutNew`(出库新单)、`TaskRelocationStatusEnum.RelocationNew`(移库新单) - 已有 WCS 调用方式:`_httpClientHelper.Post("http://localhost:9292/api/Task/ReceiveManualTask", json)` - 已有事务处理:`await _unitOfWorkManage.BeginTranAsync(async () => { ... })` - 已有 `WMSTaskDTO` 用于给 WCS 发送任务 **在 `TaskService_WCS.cs` 末尾 `#endregion WCS逻辑处理` 之前添加:** ```csharp /// /// 手动下发任务到WCS /// /// 下发任务参数列表 /// 批量下发结果 public async Task DispatchTasksToWCSAsync(List dtos) { try { if (dtos == null || !dtos.Any()) return WebResponseContent.Instance.Error("请选择要下发的任务"); var resultDto = new DispatchResultDto { SuccessCount = 0, FailCount = 0, FailResults = new List() }; 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); // 构造WMSTaskDTO发送给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( "http://localhost:9292/api/Task/ReceiveManualTask", new List { 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}"); } } /// /// 获取任务状态名称 /// 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; > **状态同步说明**:WMS 下发成功后,WMS 端任务状态保持"New"不变,不切换到执行中状态。这是现有 `CreateManualTaskAsync` 的设计模式(因为 WCS 侧有独立的任务生命周期),不跟进此 PR 改变。如需 WMS 端也同步状态,需另行评估 WCS→WMS 的状态回调机制。 using WIDESEA_Model.Models; ``` - [ ] **Commit** ```bash git add WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs git commit -m "feat(WMS): TaskService新增DispatchTasksToWCSAsync方法 - 校验任务状态,仅入库新单/出库新单/移库新单可下发 - 更新任务地址和优先级后调用WCS ReceiveManualTask接口 - 返回批量下发结果(成功/失败列表及原因) Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 3: 后端 - TaskController 新增下发接口 **文件:** - 修改: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs` **在 Controller 中添加以下方法(放在其他方法附近,建议在 CreateManualTaskAsync 之后):** ```csharp /// /// 手动下发任务到WCS /// /// 下发任务参数列表 /// 批量下发结果 [HttpGet, HttpPost, Route("DispatchTasksToWCS"), AllowAnonymous] public async Task DispatchTasksToWCSAsync([FromBody] List 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 " ``` --- ## Task 4: 前端 - 新增 dispatchTasksToWCS.vue 弹窗组件 **文件:** - 创建: `WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/dispatchTasksToWCS.vue` **参考现有 addManualTask.vue 的模式,使用 el-table 实现批量编辑:** ```vue ``` - [ ] **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 " ``` --- ## 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 " ``` --- ## 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 ``` --- ## 验收标准 - [ ] 工具栏添加"手动下发"按钮,未选中任务时点击提示"请先选择任务" - [ ] 弹窗正确显示所有选中任务 - [ ] 仅入库新单/出库新单/移库新单状态的任务可编辑地址和优先级 - [ ] 其他状态任务行标红,地址/优先级不可编辑 - [ ] 确认下发后调用后端接口 - [ ] 成功时弹窗关闭并刷新列表;失败时显示失败任务及原因 - [ ] 批量下发时正确统计成功/失败数量