CreateOrUpdateRobotTaskCommandHandler.cs 7.52 KB
using MassTransit;
using Microsoft.Extensions.Logging;
using Rcs.Application.Common;
using Rcs.Application.MessageBus.Commands;
using Rcs.Domain.Entities;
using Rcs.Domain.Repositories;
using TaskStatus = Rcs.Domain.Entities.TaskStatus;

namespace Rcs.Infrastructure.MessageBus.Handlers.Commands;

/// <summary>
/// 创建或更新任务命令处理器
/// @author zzy
/// </summary>
public class CreateOrUpdateRobotTaskCommandHandler : IConsumer<CreateOrUpdateRobotTaskCommand>
{
    private readonly ILogger<CreateOrUpdateRobotTaskCommandHandler> _logger;
    private readonly IRobotTaskRepository _robotTaskRepository;
    private readonly IRobotRepository _robotRepository;
    private readonly ITaskTemplateRepository _taskTemplateRepository;
    private readonly IStorageLocationRepository _storageLocationRepository;
    private readonly IMapResourceRepository _mapResourceRepository;

    public CreateOrUpdateRobotTaskCommandHandler(
        ILogger<CreateOrUpdateRobotTaskCommandHandler> logger,
        IRobotTaskRepository robotTaskRepository,
        IRobotRepository robotRepository,
        ITaskTemplateRepository taskTemplateRepository,
        IStorageLocationRepository storageLocationRepository,
        IMapResourceRepository mapResourceRepository)
    {
        _logger = logger;
        _robotTaskRepository = robotTaskRepository;
        _robotRepository = robotRepository;
        _taskTemplateRepository = taskTemplateRepository;
        _storageLocationRepository = storageLocationRepository;
        _mapResourceRepository = mapResourceRepository;
    }

    public async Task Consume(ConsumeContext<CreateOrUpdateRobotTaskCommand> context)
    {
        var command = context.Message;
        try
        {
            // 验证机器人(可选)
            Guid? robotId = null;
            Guid? mapId = null;
            if (!string.IsNullOrWhiteSpace(command.RobotId))
            {
                if (!Guid.TryParse(command.RobotId, out Guid parsedRobotId))
                {
                    throw new InvalidOperationException("无效的机器人ID格式");
                }
                var robot = await _robotRepository.GetByIdAsync(parsedRobotId, context.CancellationToken);
                if (robot == null)
                {
                    throw new InvalidOperationException($"机器人ID {command.RobotId} 不存在");
                }
                robotId = parsedRobotId;
                
            }

            // 验证起点库位(可选)
            Guid? beginLocationId = null;
            if (!string.IsNullOrWhiteSpace(command.BeginLocationId))
            {
                if (!Guid.TryParse(command.BeginLocationId, out Guid parsedBeginLocationId))
                {
                    throw new InvalidOperationException("无效的起点库位ID格式");
                }
                var beginLocation = await _storageLocationRepository.GetByIdAsync(parsedBeginLocationId, context.CancellationToken);
                if (beginLocation == null)
                {
                    throw new InvalidOperationException($"起点库位ID {command.BeginLocationId} 不存在");
                }
                beginLocationId = parsedBeginLocationId;
                
            }

            // 验证终点库位(可选)
            Guid? endLocationId = null;
            if (!string.IsNullOrWhiteSpace(command.EndLocationId))
            {
                if (!Guid.TryParse(command.EndLocationId, out Guid parsedEndLocationId))
                {
                    throw new InvalidOperationException("无效的终点库位ID格式");
                }
                var endLocation = await _storageLocationRepository.GetByIdAsync(parsedEndLocationId, context.CancellationToken);
                if (endLocation == null)
                {
                    throw new InvalidOperationException($"终点库位ID {command.EndLocationId} 不存在");
                }
                endLocationId = parsedEndLocationId;
            }
            // 如果未指定终点库位和区域但指定了终点资源,根据资源多边形区域查找空货位
            else if (!string.IsNullOrWhiteSpace(command.EndResourceId))
            {
                if (!Guid.TryParse(command.EndResourceId, out Guid parsedEndResourceId))
                {
                    throw new InvalidOperationException("无效的终点资源ID格式");
                }
                var resource = await _mapResourceRepository.GetByIdAsync(parsedEndResourceId, context.CancellationToken);
                if (resource == null)
                {
                    throw new InvalidOperationException($"终点资源 {command.EndResourceId} 不存在");
                }
                if (resource.LocationCoordinates == null)
                {
                    throw new InvalidOperationException($"终点资源 {command.EndResourceId} 未配置多边形区域");
                }
                var emptyLocations = await _storageLocationRepository.GetEmptyLocationsInPolygonAsync(
                    resource.MapId, resource.LocationCoordinates, context.CancellationToken);
                var emptyLocation = emptyLocations.FirstOrDefault();
                if (emptyLocation == null)
                {
                    throw new InvalidOperationException($"终点资源 {command.EndResourceId} 区域内无可用空货位");
                }
                endLocationId = emptyLocation.LocationId;
            }
            
            if (!Guid.TryParse(command.TaskId, out Guid taskId))
            {
                // 新建任务
                var existingCode = await _robotTaskRepository.GetByTaskCodeAsync(command.TaskCode, context.CancellationToken);
                if (existingCode != null)
                {
                    throw new InvalidOperationException($"任务编码 {command.TaskCode} 已存在");
                }

                var task = new RobotTask();
                task.Create(
                    command.TaskCode,
                    command.TaskName ?? command.TaskCode,
                    beginLocationId,
                    endLocationId,
                    command.Priority,
                    shelfCode: command.ShelfCode
                );
                if (robotId.HasValue)
                    task.Assign(robotId.Value);
                
                await _robotTaskRepository.AddAsync(task, context.CancellationToken);
            }
            else
            {
                // 更新任务
                var task = await _robotTaskRepository.GetByIdAsync(taskId, context.CancellationToken);
                if (task == null)
                {
                    throw new InvalidOperationException($"未找到任务ID为 {taskId} 的任务");
                }
                if (task.Status != TaskStatus.Pending)
                {
                    throw new InvalidOperationException("仅允许等待中状态的任务");
                }
                task.RobotId = robotId;
                task.BeginLocationId = beginLocationId;
                task.EndLocationId = endLocationId;
                task.Priority = command.Priority;
                task.ShelfCode = command.ShelfCode;
                task.UpdatedAt = DateTime.Now;

                await _robotTaskRepository.UpdateAsync(task, context.CancellationToken);
            }

            await context.RespondAsync(ApiResponse.Successful());
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "创建或更新任务失败");
            await context.RespondAsync(ApiResponse.Failed(ex.Message));
        }
    }
}