Initial commit
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user