ThirdController.cs 9.61 KB
using MassTransit.Internals;
using MassTransit.Mediator;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Rcs.Application.Common;
using Rcs.Application.MessageBus.Commands;
using Rcs.Application.ThirdModels;
using Rcs.Domain.Entities;
using Rcs.Domain.Repositories;

namespace Rcs.Api.OtherControllers;

/// <summary>
/// 第三方接口控制器
/// </summary>
[Route("/[controller]")]
[ApiController]
public class ThirdController : Controller
{
    private readonly ILogger<ThirdController> _logger;
    private readonly IMediator _mediator;
    private readonly IStorageLocationRepository _storageLocationRepository;
    private readonly IRobotRepository _robotRepository;

    public ThirdController(
        ILogger<ThirdController> logger,
        IMediator mediator,
        IStorageLocationRepository storageLocationRepository,
        IRobotRepository robotRepository)
    {
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _mediator = mediator;
        _storageLocationRepository = storageLocationRepository ?? throw new ArgumentNullException(nameof(storageLocationRepository));
        _robotRepository = robotRepository ?? throw new ArgumentNullException(nameof(robotRepository));
    }

    /// <summary>
    /// 任务完成接口 - 由车控系统调用,用于通知RCS任务已完成
    /// </summary>
    /// <param name="request">任务完成请求数据</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>统一响应模型</returns>
    [HttpPost("task_send")]
    [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status404NotFound)]
    public async Task<IActionResult> TaskReceive(
        [FromBody] ThirdTaskSend request, 
        CancellationToken cancellationToken = default)
    {
        // [ApiController] 特性会自动验证模型状态
        _logger.LogInformation("接收到任务发送请求: TaskId={TaskId}, LocationId={LocationId}, StoreLocationId={StoreLocationId}", 
            request.task_id, 
            request.location_id,
            request.store_location_id);

        // 1. 检查目标库位是否存在
        var targetLocation = await _storageLocationRepository
            .GetQueryable()
            .Include(l => l.MapNode)
            .FirstOrDefaultAsync(l => l.LocationCode == request.location_id && l.IsActive, cancellationToken);

        if (targetLocation == null)
        {
            _logger.LogWarning("目标库位不存在或未启用: LocationId={LocationId}", request.location_id);
            return NotFound(new ApiResponse<object>
            {
                Code = 404,
                Message = $"目标库位 '{request.location_id}' 不存在或未启用",
                Data = null
            });
        }

        // 2. 检查存储库位是否存在
        var storeLocation = await _storageLocationRepository
            .GetQueryable()
            .Include(l => l.MapNode)
            .FirstOrDefaultAsync(l => l.LocationCode == request.store_location_id && l.IsActive, cancellationToken);

        if (storeLocation == null)
        {
            _logger.LogWarning("存储库位不存在或未启用: StoreLocationId={StoreLocationId}", request.store_location_id);
            return NotFound(new ApiResponse<object>
            {
                Code = 404,
                Message = $"存储库位 '{request.store_location_id}' 不存在或未启用",
                Data = null
            });
        }

        // 3. 检查库位是否关联到地图节点
        if (targetLocation.MapNode == null)
        {
            _logger.LogWarning("目标库位未关联地图节点: LocationId={LocationId}", request.location_id);
            return BadRequest(new ApiResponse<object>
            {
                Code = 400,
                Message = $"目标库位 '{request.location_id}' 未关联地图节点",
                Data = null
            });
        }

        if (storeLocation.MapNode == null)
        {
            _logger.LogWarning("存储库位未关联地图节点: StoreLocationId={StoreLocationId}", request.store_location_id);
            return BadRequest(new ApiResponse<object>
            {
                Code = 400,
                Message = $"存储库位 '{request.store_location_id}' 未关联地图节点",
                Data = null
            });
        }

        // 获取目标库位和存储库位的地图ID
        var targetMapId = targetLocation.MapNode.MapId;
        var storeMapId = storeLocation.MapNode.MapId;

        // 4. 如果指定了机器人,检查机器人是否存在并验证地图一致性
        Robot? specifiedRobot = null;
        if (!string.IsNullOrEmpty(request.user_specified_robot_id))
        {
            specifiedRobot = await _robotRepository
                .GetQueryable()
                .FirstOrDefaultAsync(r => r.RobotCode == request.user_specified_robot_id && r.Active, cancellationToken);

            if (specifiedRobot == null)
            {
                _logger.LogWarning("指定机器人不存在或未启用: RobotId={RobotId}", request.user_specified_robot_id);
                return NotFound(new ApiResponse<object>
                {
                    Code = 404,
                    Message = $"指定机器人 '{request.user_specified_robot_id}' 不存在或未启用",
                    Data = null
                });
            }

            // 检查机器人当前地图与库位地图是否一致
            var robotMapId = specifiedRobot.CurrentMapCodeId;
            
            if (robotMapId.HasValue && robotMapId.Value != targetMapId)
            {
                _logger.LogWarning("机器人所在地图与目标库位地图不一致: RobotId={RobotId}, RobotMapId={RobotMapId}, TargetMapId={TargetMapId}",
                    request.user_specified_robot_id, robotMapId.Value, targetMapId);
                return BadRequest(new ApiResponse<object>
                {
                    Code = 400,
                    Message = $"机器人 '{request.user_specified_robot_id}' 所在地图与目标库位 '{request.location_id}' 所在地图不一致",
                    Data = new
                    {
                        RobotMapId = robotMapId.Value,
                        TargetLocationMapId = targetMapId
                    }
                });
            }

            if (robotMapId.HasValue && robotMapId.Value != storeMapId)
            {
                _logger.LogWarning("机器人所在地图与存储库位地图不一致: RobotId={RobotId}, RobotMapId={RobotMapId}, StoreMapId={StoreMapId}",
                    request.user_specified_robot_id, robotMapId.Value, storeMapId);
                return BadRequest(new ApiResponse<object>
                {
                    Code = 400,
                    Message = $"机器人 '{request.user_specified_robot_id}' 所在地图与存储库位 '{request.store_location_id}' 所在地图不一致",
                    Data = new
                    {
                        RobotMapId = robotMapId.Value,
                        StoreLocationMapId = storeMapId
                    }
                });
            }

            _logger.LogInformation("指定机器人验证通过: RobotId={RobotId}, RobotMapId={RobotMapId}",
                request.user_specified_robot_id, robotMapId);
        }
        else
        {
            // 检查两个库位是否在同一个地图
            if (targetMapId != storeMapId)
            {
                _logger.LogWarning("目标库位和存储库位不在同一地图: TargetMapId={TargetMapId}, StoreMapId={StoreMapId}",
                    targetMapId, storeMapId);
                return BadRequest(new ApiResponse<object>
                {
                    Code = 400,
                    Message = "目标库位和存储库位不在同一地图",
                    Data = new
                    {
                        TargetLocationMapId = targetMapId,
                        StoreLocationMapId = storeMapId
                    }
                });
            }
        }

        _logger.LogInformation("任务参数验证通过: TaskId={TaskId}, LocationId={LocationId}, StoreLocationId={StoreLocationId}, MapId={MapId}",
            request.task_id, request.location_id, request.store_location_id, targetMapId);

        // TODO: 实现创建任务的后续业务逻辑(如:创建 RobotTask 实体,发布创建任务命令等)
        var client = _mediator.CreateRequestClient<CreateOrUpdateRobotTaskCommand>();
        var response = await client.GetResponse<ApiResponse>(new CreateOrUpdateRobotTaskCommand
        {
            TaskCode = request.task_id,
            TaskName = request.task_id,
            BeginLocationId = targetLocation.LocationId.ToString(),
            EndLocationId = storeLocation.LocationId.ToString(),
            RobotId = specifiedRobot?.RobotId.ToString(),
            Priority = request.priority
        }, cancellationToken);

        return Ok(response);
    }
}

/// <summary>
/// 任务接收结果
/// </summary>
public class TaskReceiveResult
{
    /// <summary>
    /// 任务ID
    /// </summary>
    public string TaskId { get; set; } = string.Empty;

    /// <summary>
    /// 目标位置ID
    /// </summary>
    public string LocationId { get; set; } = string.Empty;

    /// <summary>
    /// 存储位置ID
    /// </summary>
    public string StoreLocationId { get; set; } = string.Empty;

    /// <summary>
    /// 地图ID
    /// </summary>
    public Guid MapId { get; set; }

    /// <summary>
    /// 指定机器人ID(如有)
    /// </summary>
    public string? RobotId { get; set; }
}