编辑 | blame | 历史 | 原始文档

手动创建任务功能实施计划

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后,WCS判断起点为线体点位(11068/11010/11001)时写入输送线任务。

Architecture: WMS前端Vue页面 → WMS后端API → WCS ReceiveTask → WCS FlowService判断并写入输送线PLC

Tech Stack: .NET 6 (WMS/WCS Backend), Vue3 (WMS Frontend), MapsterMapper, SqlSugar


文件结构

文件 职责
WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js 添加手动创建任务按钮和处理逻辑
WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/CreateManualTaskDto.cs 新建 - 手动创建任务请求DTO
WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs 添加 CreateManualTask 接口
WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs 添加 CreateManualTaskAsync 方法
WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs ReceiveWMSTask 已有分发逻辑,确认入口正确
WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/InboundTaskFlowService.cs 添加线体点位判断和输送线任务写入逻辑

WMS 前端

Task 1: 添加手动创建任务按钮

文件:
- Modify: WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js:16

  • [ ] Step 1: 在 buttons.box 添加手动创建按钮

task.jsbuttons: { view: [], box: [], detail: [] } 中添加:

buttons: {
  view: [],
  box: [
    {
      value: 'CreateManualTask',
      label: '手动创建任务',
      onClick: function () {
        // 弹出手动创建任务对话框
        this.$refs.grid.openModel('Add');
      }
    }
  ],
  detail: []
},
  • [ ] Step 2: 在 modelFooter 添加自定义弹窗

components.modelFooter 添加 Vue 模板(如果框架支持自定义弹窗内容),或使用框架内置的 openModel 配合 addBefore 处理。

注意: 具体的弹窗实现取决于当前前端框架的扩展机制。若当前页面不支持自定义字段,则需要在 task.vue 中添加新的页面组件,或通过 modelBody 扩展自定义表单。

  • [ ] Step 3: 提交
git add WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js
git commit -m "feat(WMS): 添加手动创建任务按钮"

WMS 后端

Task 2: 创建 DTO

文件:
- Create: WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/CreateManualTaskDto.cs

  • [ ] Step 1: 编写 CreateManualTaskDto
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
using WIDESEA_Common.TaskEnum;

namespace WIDESEA_DTO.Task
{
    /// <summary>
    /// 手动创建任务请求DTO
    /// </summary>
    public class CreateManualTaskDto
    {
        /// <summary>
        /// 任务类型:1=入库, 2=出库, 3=移库
        /// </summary>
        [JsonPropertyName("taskType")]
        [Required(ErrorMessage = "任务类型不能为空")]
        public TaskTypeEnum TaskType { get; set; }

        /// <summary>
        /// 起点地址
        /// </summary>
        [JsonPropertyName("sourceAddress")]
        [Required(ErrorMessage = "起点地址不能为空")]
        public string SourceAddress { get; set; }

        /// <summary>
        /// 终点地址
        /// </summary>
        [JsonPropertyName("targetAddress")]
        [Required(ErrorMessage = "终点地址不能为空")]
        public string TargetAddress { get; set; }

        /// <summary>
        /// 条码
        /// </summary>
        [JsonPropertyName("barcode")]
        [Required(ErrorMessage = "条码不能为空")]
        public string Barcode { get; set; }

        /// <summary>
        /// 仓库ID
        /// </summary>
        [JsonPropertyName("warehouseId")]
        [Required(ErrorMessage = "仓库ID不能为空")]
        public int WarehouseId { get; set; }

        /// <summary>
        /// 优先级,默认1
        /// </summary>
        [JsonPropertyName("grade")]
        public int Grade { get; set; } = 1;
    }
}
  • [ ] Step 2: 提交
git add WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/CreateManualTaskDto.cs
git commit -m "feat(WMS): 新增CreateManualTaskDto"

Task 3: 添加 Controller 接口

文件:
- Modify: WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs

  • [ ] Step 1: 添加 CreateManualTask 接口

TaskController 类中添加:

/// <summary>
/// 手动创建任务
/// </summary>
/// <param name="dto">手动创建任务参数</param>
/// <returns></returns>
[HttpGet, HttpPost, Route("CreateManualTask"), AllowAnonymous]
public async Task<WebResponseContent?> CreateManualTaskAsync([FromBody] CreateManualTaskDto dto)
{
    return await Service.CreateManualTaskAsync(dto);
}
  • [ ] Step 2: 提交
git add WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs
git commit -m "feat(WMS): 添加手动创建任务接口 CreateManualTask"

Task 4: 添加 TaskService 方法

文件:
- Modify: WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs

首先查看现有 TaskService.cs 结构,确认方法签名格式。

  • [ ] Step 1: 添加 CreateManualTaskAsync 方法

TaskService 类中添加(参考 CreateAutoOutboundTasksAsync 的模式):

/// <summary>
/// 手动创建任务
/// </summary>
/// <param name="dto">手动创建任务参数</param>
/// <returns></returns>
public async Task<WebResponseContent> CreateManualTaskAsync(CreateManualTaskDto dto)
{
    try
    {
        // 1. 生成任务号
        int taskNum = await BaseDal.GetTaskNo();

        // 2. 根据任务类型确定状态值
        int taskStatus = dto.TaskType switch
        {
            TaskTypeEnum.Inbound => TaskInStatusEnum.InNew.GetHashCode(),    // 200
            TaskTypeEnum.Outbound => TaskOutStatusEnum.OutNew.GetHashCode(),  // 100
            TaskTypeEnum.Relocation => TaskRelocationStatusEnum.New.GetHashCode(), // 300
            _ => TaskStatusEnum.New.GetHashCode()
        };

        // 3. 构建任务实体
        var task = new Dt_Task
        {
            TaskNum = taskNum,
            PalletCode = dto.Barcode,
            SourceAddress = dto.SourceAddress,
            TargetAddress = dto.TargetAddress,
            TaskType = dto.TaskType.GetHashCode(),
            TaskStatus = taskStatus,
            Grade = dto.Grade,
            WarehouseId = dto.WarehouseId,
            Creater = "manual",
            CreateDate = DateTime.Now,
            ModifyDate = DateTime.Now
        };

        // 4. 保存到数据库
        var result = await BaseDal.Add(task);
        if (!result)
            return WebResponseContent.Instance.Error("创建任务失败");

        // 5. 发送任务到WCS
        var wmsTaskDto = new WMSTaskDTO
        {
            TaskNum = task.TaskNum,
            PalletCode = task.PalletCode,
            SourceAddress = task.SourceAddress,
            TargetAddress = task.TargetAddress,
            TaskType = task.TaskType,
            TaskStatus = task.TaskStatus,
            WarehouseId = task.WarehouseId ?? 0
        };

        var wcsResult = await _httpClientHelper.PostAsync<WebResponseContent>(
            "http://localhost:9292/api/Task/ReceiveTask",
            wmsTaskDto.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}");
    }
}

注意: 需要确认 TaskRelocationStatusEnum 是否存在,如不存在则使用 TaskStatusEnum.New

  • [ ] Step 2: 添加必要的 using
using WIDESEA_DTO.Task;
using WIDESEA_Common.TaskEnum;
using WIDESEAWCS_DTO;
  • [ ] Step 3: 提交
git add WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
git commit -m "feat(WMS): 添加手动创建任务 CreateManualTaskAsync 方法"

WCS 后端

Task 5: 确认 ReceiveWMSTask 入口

文件:
- Modify: WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs

  • [ ] Step 1: 确认 ReceiveWMSTask 方法存在

确认 ReceiveWMSTask 接收 WMSTaskDTO 列表后路由到对应 FlowService。

public WebResponseContent ReceiveWMSTask([NotNull] List<WMSTaskDTO> taskDTOs)
{
    // 遍历任务,根据 TaskType 分发到不同 FlowService
    foreach (var item in taskDTOs)
    {
        // 根据 item.TaskType 判断路由到 Inbound/Outbound/Relocation
    }
}
  • [ ] Step 2: 提交
git add WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs
git commit -m "feat(WCS): 确认 ReceiveWMSTask 入口正确"

Task 6: 在 InboundTaskFlowService 添加线体点位处理

文件:
- Modify: WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/InboundTaskFlowService.cs

首先查看现有的 InitializeOnReceive 方法完整实现。

  • [ ] Step 1: 添加线体点位常量

在类中添加:

/// <summary>
/// 线体入库点位
/// </summary>
private static readonly string[] LINE_IN_POINTS = { "11068", "11010", "11001" };
  • [ ] Step 2: 修改 InitializeOnReceive 方法

InitializeOnReceive 方法中,判断如果 SourceAddress 是线体点位,则写入输送线任务:

public void InitializeOnReceive([NotNull] Dt_Task task, [NotNull] WMSTaskDTO source)
{
    // 先执行原有的路由逻辑
    Dt_Router routers = _routerService.QueryNextRoute(source.SourceAddress);
    if (routers.IsNullOrEmpty())
    {
        return;
    }

    task.TaskStatus = (int)TaskInStatusEnum.InNew;
    task.CurrentAddress = source.SourceAddress;
    task.NextAddress = routers.ChildPosi;

    // 如果起点是线体点位(11068/11010/11001),直接写入输送线任务
    if (LINE_IN_POINTS.Contains(source.SourceAddress))
    {
        WriteConveyorLineTask(task, source.SourceAddress);
    }
}

/// <summary>
/// 写入输送线任务到PLC
/// </summary>
private void WriteConveyorLineTask(Dt_Task task, string sourceAddress)
{
    // 1. 通过 Storage.Devices 查找对应的输送线设备
    IDevice? device = Storage.Devices
        .FirstOrDefault(x => x.DeviceProDTOs.Any(d => d.DeviceChildCode == sourceAddress));

    if (device == null)
    {
        // 记录日志:未找到对应的输送线设备
        QuartzLogger.Error($"手动创建任务:未找到源地址【{sourceAddress}】对应的输送线设备", "InboundTaskFlowService");
        return;
    }

    // 2. 转换为 CommonConveyorLine 类型
    if (device is not CommonConveyorLine conveyorLine)
    {
        QuartzLogger.Error($"设备【{device.DeviceCode}】不是输送线类型", "InboundTaskFlowService");
        return;
    }

    // 3. 获取子设备编码
    string? childDeviceCode = device.DeviceProDTOs
        .FirstOrDefault(d => d.DeviceChildCode == sourceAddress)?.DeviceChildCode;

    if (string.IsNullOrEmpty(childDeviceCode))
    {
        QuartzLogger.Error($"源地址【{sourceAddress}】未找到对应的子设备编码", "InboundTaskFlowService");
        return;
    }

    // 4. 构造 ConveyorLineTaskCommandNew
    ConveyorLineTaskCommandNew command = new ConveyorLineTaskCommandNew
    {
        TaskNo = (short)task.TaskNum,
        Source = short.Parse(sourceAddress),
        Target = short.Parse(task.TargetAddress ?? "0"),
        Barcode = task.PalletCode ?? string.Empty,
        WCS_STB = 1,  // WCS已发送标志
        WCS_ACK = 0,
        PLC_STB = 0,
        PLC_ACK = 0
    };

    // 5. 写入PLC
    bool success = conveyorLine.SendCommand(command, childDeviceCode);
    if (success)
    {
        QuartzLogger.Info($"手动创建入库任务已写入输送线:任务号【{task.TaskNum}】,源地址【{sourceAddress}】", conveyorLine.DeviceCode);
    }
    else
    {
        QuartzLogger.Error($"手动创建入库任务写入输送线失败:任务号【{task.TaskNum}】,源地址【{sourceAddress}】", conveyorLine.DeviceCode);
    }
}
  • [ ] Step 3: 添加必要的 using
using WIDESEAWCS_QuartzJob;
using WIDESEAWCS_QuartzJob.ConveyorLine;
using WIDESEAWCS_Tasks;
using WIDESEAWCS_Core.LogHelper;
  • [ ] Step 4: 提交
git add WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/InboundTaskFlowService.cs
git commit -m "feat(WCS): 入库任务线体点位时写入输送线任务"

验证清单

  • [ ] WMS 前端:点击"手动创建任务"按钮弹出对话框
  • [ ] WMS 前端:填写表单后提交,返回成功
  • [ ] WMS 后端:创建任务写入数据库
  • [ ] WMS 后端:任务成功发送给 WCS
  • [ ] WCS 后端:ReceiveTask 收到任务
  • [ ] WCS 后端:起点为线体点位时,任务写入 PLC 成功
  • [ ] WCS 后端:起点为普通点位时,走原有路由逻辑