307 lines
11 KiB
C#
307 lines
11 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.Extensions.Logging;
|
|
using RobotNet10.MapEditor.Shared.DTOs.VehicleType;
|
|
using RobotNet10.MapEditor.Shared.DTOs.Requests;
|
|
using RobotNet10.MapEditor.Shared.DTOs.Responses;
|
|
using RobotNet10.MapManager.Services;
|
|
|
|
namespace RobotNet10.MapManager.Controllers;
|
|
|
|
/// <summary>
|
|
/// Controller for managing vehicle types
|
|
/// </summary>
|
|
[ApiController]
|
|
[Route("api/vehicles")]
|
|
[Authorize]
|
|
public class VehiclesManagerController(
|
|
IVehicleTypeService vehicleTypeService,
|
|
ILogger<VehiclesManagerController> logger) : ControllerBase
|
|
{
|
|
private readonly IVehicleTypeService _vehicleTypeService = vehicleTypeService;
|
|
private readonly ILogger<VehiclesManagerController> _logger = logger;
|
|
|
|
/// <summary>
|
|
/// Create a new vehicle type
|
|
/// </summary>
|
|
/// <param name="request">Vehicle type creation request</param>
|
|
/// <returns>Created vehicle type</returns>
|
|
[HttpPost]
|
|
[ProducesResponseType(typeof(VehicleTypeDto), 201)]
|
|
[ProducesResponseType(400)]
|
|
public async Task<ActionResult<VehicleTypeDto>> CreateVehicleType([FromBody] CreateVehicleTypeRequest request)
|
|
{
|
|
try
|
|
{
|
|
var vehicleType = await _vehicleTypeService.CreateAsync(
|
|
request.VehicleTypeId,
|
|
request.VehicleTypeName,
|
|
request.Description,
|
|
request.Specifications,
|
|
request.Actions);
|
|
|
|
var dto = MapToDto(vehicleType);
|
|
|
|
return CreatedAtAction(
|
|
nameof(GetVehicleType),
|
|
new { vehicleTypeId = vehicleType.Id },
|
|
dto);
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
_logger.LogWarning(ex, "Failed to create vehicle type: {VehicleTypeId}", request.VehicleTypeId);
|
|
var errorCode = ex.Message.Contains("already exists")
|
|
? "VEHICLE_TYPE_ALREADY_EXISTS"
|
|
: "VALIDATION_ERROR";
|
|
return BadRequest(CreateErrorResponse(ex.Message, errorCode));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get all vehicle types
|
|
/// </summary>
|
|
/// <param name="isActive">Optional filter by active status</param>
|
|
/// <returns>List of vehicle types</returns>
|
|
[HttpGet]
|
|
[ProducesResponseType(typeof(List<VehicleTypeDto>), 200)]
|
|
public async Task<ActionResult<List<VehicleTypeDto>>> GetAllVehicleTypes([FromQuery] bool? isActive)
|
|
{
|
|
List<Data.VehicleType> vehicleTypes;
|
|
|
|
if (isActive.HasValue)
|
|
{
|
|
vehicleTypes = await _vehicleTypeService.GetByActiveStatusAsync(isActive.Value);
|
|
}
|
|
else
|
|
{
|
|
vehicleTypes = await _vehicleTypeService.GetAllAsync();
|
|
}
|
|
|
|
var dtos = vehicleTypes.Select(MapToDto).ToList();
|
|
|
|
return Ok(dtos);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get vehicle type by database ID
|
|
/// </summary>
|
|
/// <param name="vehicleTypeId">Vehicle type database ID</param>
|
|
/// <returns>Vehicle type details</returns>
|
|
[HttpGet("{vehicleTypeId}")]
|
|
[ProducesResponseType(typeof(VehicleTypeDto), 200)]
|
|
[ProducesResponseType(404)]
|
|
public async Task<ActionResult<VehicleTypeDto>> GetVehicleType(Guid vehicleTypeId)
|
|
{
|
|
var vehicleType = await _vehicleTypeService.GetByIdAsync(vehicleTypeId);
|
|
|
|
if (vehicleType == null)
|
|
{
|
|
return NotFound(CreateErrorResponse(
|
|
$"Vehicle type with ID '{vehicleTypeId}' not found",
|
|
"VEHICLE_TYPE_NOT_FOUND"));
|
|
}
|
|
|
|
return Ok(MapToDto(vehicleType));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get vehicle type by VehicleTypeId string
|
|
/// </summary>
|
|
/// <param name="vehicleTypeId">Vehicle type identifier string (e.g., "AMR-T800")</param>
|
|
/// <returns>Vehicle type details</returns>
|
|
[HttpGet("vehicleTypeId/{vehicleTypeId}")]
|
|
[ProducesResponseType(typeof(VehicleTypeDto), 200)]
|
|
[ProducesResponseType(404)]
|
|
public async Task<ActionResult<VehicleTypeDto>> GetVehicleTypeByStringId(string vehicleTypeId)
|
|
{
|
|
var vehicleType = await _vehicleTypeService.GetByVehicleTypeIdAsync(vehicleTypeId);
|
|
|
|
if (vehicleType == null)
|
|
{
|
|
return NotFound(CreateErrorResponse(
|
|
$"Vehicle type with VehicleTypeId '{vehicleTypeId}' not found",
|
|
"VEHICLE_TYPE_NOT_FOUND"));
|
|
}
|
|
|
|
return Ok(MapToDto(vehicleType));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Search vehicle types by query string
|
|
/// Searches in VehicleTypeId and VehicleTypeName (case-insensitive)
|
|
/// </summary>
|
|
/// <param name="query">Search query</param>
|
|
/// <returns>List of matching vehicle types</returns>
|
|
[HttpGet("search")]
|
|
[ProducesResponseType(typeof(List<VehicleTypeDto>), 200)]
|
|
public async Task<ActionResult<List<VehicleTypeDto>>> SearchVehicleTypes([FromQuery] string query)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(query))
|
|
{
|
|
return BadRequest(CreateErrorResponse(
|
|
"Query parameter is required",
|
|
"VALIDATION_ERROR"));
|
|
}
|
|
|
|
var vehicleTypes = await _vehicleTypeService.SearchAsync(query);
|
|
var dtos = vehicleTypes.Select(MapToDto).ToList();
|
|
|
|
return Ok(dtos);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get usage information for a vehicle type
|
|
/// </summary>
|
|
/// <param name="id">Vehicle type database ID</param>
|
|
/// <returns>Usage information</returns>
|
|
[HttpGet("{id}/usage")]
|
|
[ProducesResponseType(typeof(VehicleTypeUsageInfoDto), 200)]
|
|
[ProducesResponseType(404)]
|
|
public async Task<ActionResult<VehicleTypeUsageInfoDto>> GetVehicleTypeUsage(Guid id)
|
|
{
|
|
try
|
|
{
|
|
var usageInfo = await _vehicleTypeService.GetUsageInfoAsync(id);
|
|
var dto = MapUsageInfoToDto(usageInfo);
|
|
|
|
return Ok(dto);
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
_logger.LogWarning(ex, "Failed to get usage info for vehicle type: {Id}", id);
|
|
return NotFound(CreateErrorResponse(ex.Message, "VEHICLE_TYPE_NOT_FOUND"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update vehicle type
|
|
/// </summary>
|
|
/// <param name="vehicleTypeId">Vehicle type database ID</param>
|
|
/// <param name="request">Update request</param>
|
|
/// <returns>Updated vehicle type</returns>
|
|
[HttpPut("{vehicleTypeId}")]
|
|
[ProducesResponseType(typeof(VehicleTypeDto), 200)]
|
|
[ProducesResponseType(404)]
|
|
public async Task<ActionResult<VehicleTypeDto>> UpdateVehicleType(
|
|
Guid vehicleTypeId,
|
|
[FromBody] UpdateVehicleTypeRequest request)
|
|
{
|
|
try
|
|
{
|
|
var vehicleType = await _vehicleTypeService.UpdateAsync(
|
|
vehicleTypeId,
|
|
request.VehicleTypeName,
|
|
request.Description,
|
|
request.Specifications,
|
|
request.Actions,
|
|
request.IsActive);
|
|
|
|
return Ok(MapToDto(vehicleType));
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
if (_logger.IsEnabled(LogLevel.Warning)) _logger.LogWarning(ex, "Failed to update vehicle type: {VehicleTypeId}", vehicleTypeId);
|
|
return NotFound(CreateErrorResponse(ex.Message, "VEHICLE_TYPE_NOT_FOUND"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delete vehicle type
|
|
/// </summary>
|
|
/// <param name="vehicleTypeId">Vehicle type database ID</param>
|
|
/// <returns>No content on success</returns>
|
|
[HttpDelete("{vehicleTypeId}")]
|
|
[ProducesResponseType(204)]
|
|
[ProducesResponseType(404)]
|
|
[ProducesResponseType(400)]
|
|
public async Task<IActionResult> DeleteVehicleType(Guid vehicleTypeId)
|
|
{
|
|
try
|
|
{
|
|
var deleted = await _vehicleTypeService.DeleteAsync(vehicleTypeId);
|
|
|
|
if (!deleted)
|
|
{
|
|
return NotFound(CreateErrorResponse(
|
|
$"Vehicle type with ID '{vehicleTypeId}' not found",
|
|
"VEHICLE_TYPE_NOT_FOUND"));
|
|
}
|
|
|
|
return NoContent();
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
if (_logger.IsEnabled(LogLevel.Warning)) _logger.LogWarning(ex, "Failed to delete vehicle type: {VehicleTypeId}", vehicleTypeId);
|
|
|
|
var errorCode = ex.Message.Contains("referenced")
|
|
? "VEHICLE_TYPE_IN_USE"
|
|
: "VALIDATION_ERROR";
|
|
|
|
var details = new Dictionary<string, object>();
|
|
if (errorCode == "VEHICLE_TYPE_IN_USE")
|
|
{
|
|
// Try to get usage info for details
|
|
try
|
|
{
|
|
var usageInfo = await _vehicleTypeService.GetUsageInfoAsync(vehicleTypeId);
|
|
details["nodePropertiesCount"] = usageInfo.NodePropertiesCount;
|
|
details["edgePropertiesCount"] = usageInfo.EdgePropertiesCount;
|
|
details["totalUsageCount"] = usageInfo.TotalUsageCount;
|
|
}
|
|
catch
|
|
{
|
|
// Ignore if we can't get usage info
|
|
}
|
|
}
|
|
|
|
return BadRequest(CreateErrorResponse(ex.Message, errorCode, details));
|
|
}
|
|
}
|
|
|
|
// Helper method to map entity to DTO
|
|
private static VehicleTypeDto MapToDto(Data.VehicleType vehicleType)
|
|
{
|
|
return new VehicleTypeDto
|
|
{
|
|
Id = vehicleType.Id,
|
|
VehicleTypeId = vehicleType.VehicleTypeId,
|
|
VehicleTypeName = vehicleType.VehicleTypeName,
|
|
Description = vehicleType.Description,
|
|
Specifications = vehicleType.Specifications,
|
|
Actions = vehicleType.Actions,
|
|
IsActive = vehicleType.IsActive,
|
|
CreatedDate = vehicleType.CreatedDate
|
|
};
|
|
}
|
|
|
|
// Helper method to map usage info to DTO
|
|
private static VehicleTypeUsageInfoDto MapUsageInfoToDto(VehicleTypeUsageInfo usageInfo)
|
|
{
|
|
return new VehicleTypeUsageInfoDto
|
|
{
|
|
VehicleTypeId = usageInfo.VehicleTypeId,
|
|
VehicleTypeIdString = usageInfo.VehicleTypeIdString,
|
|
VehicleTypeName = usageInfo.VehicleTypeName,
|
|
NodePropertiesCount = usageInfo.NodePropertiesCount,
|
|
EdgePropertiesCount = usageInfo.EdgePropertiesCount,
|
|
TotalUsageCount = usageInfo.TotalUsageCount,
|
|
CanDelete = usageInfo.CanDelete
|
|
};
|
|
}
|
|
|
|
// Helper method to create error response
|
|
private static ErrorResponseDto CreateErrorResponse(
|
|
string error,
|
|
string? errorCode = null,
|
|
Dictionary<string, object>? details = null)
|
|
{
|
|
return new ErrorResponseDto
|
|
{
|
|
Error = error,
|
|
ErrorCode = errorCode,
|
|
Details = details
|
|
};
|
|
}
|
|
}
|
|
|