Initial commit

This commit is contained in:
2026-07-03 16:37:12 +07:00
commit 63b8c1ea8b
1931 changed files with 640587 additions and 0 deletions

View File

@@ -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
};
}
}