172 lines
6.3 KiB
C#
172 lines
6.3 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.Extensions.Logging;
|
|
using RobotNet10.MapEditor.Shared.DTOs.LayoutData;
|
|
using RobotNet10.MapEditor.Shared.DTOs.Requests;
|
|
using RobotNet10.MapEditor.Shared.DTOs.Responses;
|
|
using RobotNet10.MapManager.Services;
|
|
|
|
namespace RobotNet10.MapManager.Controllers;
|
|
|
|
/// <summary>
|
|
/// Controller for retrieving complete layout data and merge/split operations
|
|
/// </summary>
|
|
[ApiController]
|
|
[Route("api/layout-data")]
|
|
[Authorize]
|
|
public class LayoutDataController(
|
|
ILayoutDataService layoutDataService,
|
|
ILogger<LayoutDataController> logger) : ControllerBase
|
|
{
|
|
private readonly ILayoutDataService _layoutDataService = layoutDataService;
|
|
private readonly ILogger<LayoutDataController> _logger = logger;
|
|
|
|
/// <summary>
|
|
/// Get complete layout data for a layout level
|
|
/// Returns all nodes, edges, and stations with full nested properties
|
|
/// </summary>
|
|
/// <param name="layoutLevelId">Layout level ID</param>
|
|
/// <returns>Complete layout data</returns>
|
|
[HttpGet("{layoutLevelId}")]
|
|
[ProducesResponseType(typeof(LayoutDataDto), 200)]
|
|
public async Task<ActionResult<LayoutDataDto>> GetLayoutData(Guid layoutLevelId)
|
|
{
|
|
try
|
|
{
|
|
var data = await _layoutDataService.GetLayoutDataAsync(layoutLevelId);
|
|
|
|
return Ok(data);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (_logger.IsEnabled(LogLevel.Error)) _logger.LogError(ex, "Error retrieving layout data for level {LevelId}", layoutLevelId);
|
|
return StatusCode(500, CreateErrorResponse("Internal server error while retrieving layout data", "INTERNAL_ERROR"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Merge multiple nodes into one node at center position
|
|
/// </summary>
|
|
/// <param name="request">Merge nodes request</param>
|
|
/// <returns>Merge result with merged node, updated edges, and deleted node IDs</returns>
|
|
[HttpPost("merge-nodes")]
|
|
[ProducesResponseType(typeof(MergeNodesResponse), 200)]
|
|
[ProducesResponseType(400)]
|
|
public async Task<ActionResult<MergeNodesResponse>> MergeNodes([FromBody] MergeNodesRequest request)
|
|
{
|
|
try
|
|
{
|
|
var result = await _layoutDataService.MergeNodesAsync(request);
|
|
return Ok(result);
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
_logger.LogWarning(ex, "Failed to merge nodes");
|
|
return BadRequest(CreateErrorResponse(ex.Message, "VALIDATION_ERROR"));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error merging nodes");
|
|
return StatusCode(500, CreateErrorResponse("Internal server error while merging nodes", "INTERNAL_ERROR"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Split a node into multiple nodes (one for each connected edge)
|
|
/// </summary>
|
|
/// <param name="request">Split node request</param>
|
|
/// <returns>Split result with new nodes, updated edges, and deleted node ID</returns>
|
|
[HttpPost("split-node")]
|
|
[ProducesResponseType(typeof(SplitNodeResponse), 200)]
|
|
[ProducesResponseType(400)]
|
|
public async Task<ActionResult<SplitNodeResponse>> SplitNode([FromBody] SplitNodeRequest request)
|
|
{
|
|
try
|
|
{
|
|
var result = await _layoutDataService.SplitNodeAsync(request);
|
|
return Ok(result);
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
_logger.LogWarning(ex, "Failed to split node");
|
|
return BadRequest(CreateErrorResponse(ex.Message, "VALIDATION_ERROR"));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error splitting node");
|
|
return StatusCode(500, CreateErrorResponse("Internal server error while splitting node", "INTERNAL_ERROR"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Save all layout changes (nodes and edges) in a batch operation
|
|
/// Uses transaction to ensure atomicity
|
|
/// </summary>
|
|
/// <param name="request">Save request with nodes and edges to update</param>
|
|
/// <returns>Save result with counts and any skipped items</returns>
|
|
[HttpPost("save")]
|
|
[ProducesResponseType(typeof(SaveLayoutDataResponse), 200)]
|
|
[ProducesResponseType(400)]
|
|
public async Task<ActionResult<SaveLayoutDataResponse>> SaveLayoutData([FromBody] SaveLayoutDataRequest request)
|
|
{
|
|
try
|
|
{
|
|
var result = await _layoutDataService.SaveLayoutDataAsync(request);
|
|
|
|
if (!result.Success)
|
|
{
|
|
return BadRequest(result);
|
|
}
|
|
|
|
return Ok(result);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error saving layout data");
|
|
return StatusCode(500, CreateErrorResponse("Internal server error while saving layout data", "INTERNAL_ERROR"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copy selected nodes and edges with an offset
|
|
/// Creates new nodes and edges at offset positions
|
|
/// </summary>
|
|
/// <param name="request">Copy request with node IDs, edge IDs, and offset</param>
|
|
/// <returns>Copy result with newly created nodes and edges</returns>
|
|
[HttpPost("copy-nodes")]
|
|
[ProducesResponseType(typeof(CopyNodesResponse), 200)]
|
|
[ProducesResponseType(400)]
|
|
public async Task<ActionResult<CopyNodesResponse>> CopyNodes([FromBody] CopyNodesRequest request)
|
|
{
|
|
try
|
|
{
|
|
var result = await _layoutDataService.CopyNodesAsync(request);
|
|
|
|
if (!result.Success)
|
|
{
|
|
return BadRequest(result);
|
|
}
|
|
|
|
return Ok(result);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error copying nodes");
|
|
return StatusCode(500, CreateErrorResponse("Internal server error while copying nodes", "INTERNAL_ERROR"));
|
|
}
|
|
}
|
|
|
|
private static ErrorResponseDto CreateErrorResponse(
|
|
string error,
|
|
string? errorCode = null,
|
|
Dictionary<string, object>? details = null)
|
|
{
|
|
return new ErrorResponseDto
|
|
{
|
|
Error = error,
|
|
ErrorCode = errorCode,
|
|
Details = details
|
|
};
|
|
}
|
|
}
|