Initial commit

This commit is contained in:
2026-07-13 09:25:40 +07:00
parent c08ff54676
commit bccfb156d7
1938 changed files with 641646 additions and 0 deletions

View File

@@ -0,0 +1,207 @@
using Microsoft.EntityFrameworkCore;
using RobotNet10.FleetManager.Data;
using RobotNet10.FleetManager.Events;
using RobotNet10.FleetManager.Events.Events;
using RobotNet10.FleetManager.Shared.DTOs.RobotModel;
namespace RobotNet10.FleetManager.Services;
/// <summary>
/// Service implementation for managing robot models.
/// Handles business logic for CRUD operations on robot models.
/// </summary>
/// <remarks>
/// This service validates business rules such as duplicate model names,
/// checks for associated robots before deletion, and provides search functionality.
/// </remarks>
public class RobotModelService(
ApplicationDbContext context,
Logger<RobotModelService> logger,
IRobotEventBus? eventBus = null) : IRobotModelService
{
private readonly ApplicationDbContext _context = context;
private readonly Logger<RobotModelService> _logger = logger;
private readonly IRobotEventBus? _eventBus = eventBus;
public async Task<RobotModel> CreateAsync(CreateRobotModelRequest request)
{
// Check if model name already exists
if (await ExistsAsync(request.ModelName))
{
throw new InvalidOperationException($"Robot model with name '{request.ModelName}' already exists.");
}
var robotModel = new RobotModel
{
Id = Guid.NewGuid(),
ModelName = request.ModelName,
Length = request.Length,
Width = request.Width,
ImageWidth = request.ImageWidth,
ImageHeight = request.ImageHeight,
NavigationPointX = request.NavigationPointX,
NavigationPointY = request.NavigationPointY,
NavigationType = request.NavigationType,
VehicleTypeId = request.VehicleTypeId,
CreatedDate = DateTime.UtcNow
};
_context.RobotModels.Add(robotModel);
await _context.SaveChangesAsync();
_logger.Info($"Created robot model {robotModel.ModelName} with ID {robotModel.Id}");
return robotModel;
}
public async Task<List<RobotModel>> GetAllAsync()
{
return await _context.RobotModels
.AsNoTracking()
.OrderBy(rm => rm.ModelName)
.ToListAsync();
}
public async Task<RobotModel?> GetByIdAsync(Guid id)
{
return await _context.RobotModels
.AsNoTracking()
.Include(rm => rm.Robots)
.FirstOrDefaultAsync(rm => rm.Id == id);
}
public async Task<RobotModel> UpdateAsync(Guid id, UpdateRobotModelRequest request)
{
var robotModel = await _context.RobotModels.FindAsync(id) ?? throw new KeyNotFoundException($"Robot model with ID {id} not found.");
// Check if model name is being changed and if new name already exists
if (request.ModelName != null && request.ModelName != robotModel.ModelName)
{
if (await ExistsAsync(request.ModelName))
{
throw new InvalidOperationException($"Robot model with name '{request.ModelName}' already exists.");
}
robotModel.ModelName = request.ModelName;
}
if (request.Length.HasValue)
robotModel.Length = request.Length.Value;
if (request.Width.HasValue)
robotModel.Width = request.Width.Value;
if (request.ImageWidth.HasValue)
robotModel.ImageWidth = request.ImageWidth.Value;
if (request.ImageHeight.HasValue)
robotModel.ImageHeight = request.ImageHeight.Value;
if (request.NavigationPointX.HasValue)
robotModel.NavigationPointX = request.NavigationPointX.Value;
if (request.NavigationPointY.HasValue)
robotModel.NavigationPointY = request.NavigationPointY.Value;
if (request.NavigationType.HasValue)
robotModel.NavigationType = request.NavigationType.Value;
// VehicleTypeId: nullable, can be set or cleared
// Note: In ASP.NET Core, if VehicleTypeId is in the JSON request, it will be bound
// If not in JSON, it remains the default (null for nullable Guid?)
// For simplicity, we always update VehicleTypeId if the request contains it
// This allows setting to null by sending "VehicleTypeId": null in JSON
// To avoid updating when not provided, we'd need a different approach (e.g., use a wrapper DTO)
// For now, we'll always update if the property exists in the request object
robotModel.VehicleTypeId = request.VehicleTypeId;
robotModel.UpdatedDate = DateTime.UtcNow;
await _context.SaveChangesAsync();
_logger.Info($"Updated robot model {robotModel.ModelName} with ID {robotModel.Id}");
// Publish event for cache invalidation
if (_eventBus != null)
{
try
{
// Get all robots using this model
var affectedRobots = await _context.Robots
.Where(r => r.ModelId == robotModel.Id)
.Select(r => r.RobotId)
.ToListAsync();
var eventData = new RobotModelUpdatedEvent
{
ModelId = robotModel.Id,
AffectedRobotIds = affectedRobots
};
_eventBus.PublishRobotModelUpdated(eventData);
_logger.Debug($"Published RobotModelUpdated event for ModelId {robotModel.Id} affecting {affectedRobots.Count} robot(s)");
}
catch (Exception ex)
{
_logger.Error($"Error publishing RobotModelUpdated event: {ex.Message}");
// Don't fail the update if event publishing fails
}
}
return robotModel;
}
public async Task<bool> DeleteAsync(Guid id)
{
var robotModel = await _context.RobotModels
.Include(rm => rm.Robots)
.FirstOrDefaultAsync(rm => rm.Id == id);
if (robotModel == null)
{
return false;
}
// Check if there are any robots using this model
if (robotModel.Robots.Count != 0)
{
throw new InvalidOperationException($"Cannot delete robot model '{robotModel.ModelName}' because it is being used by {robotModel.Robots.Count} robot(s).");
}
_context.RobotModels.Remove(robotModel);
await _context.SaveChangesAsync();
_logger.Info($"Deleted robot model {robotModel.ModelName} with ID {robotModel.Id}");
return true;
}
public async Task<bool> ExistsAsync(string modelName)
{
return await _context.RobotModels
.AnyAsync(rm => rm.ModelName == modelName);
}
public async Task<List<RobotModel>> SearchAsync(string query)
{
if (string.IsNullOrWhiteSpace(query))
{
return await GetAllAsync();
}
var lowerQuery = query.ToLowerInvariant();
return await _context.RobotModels
.AsNoTracking()
.Where(rm => rm.ModelName.ToLower().Contains(lowerQuery))
.OrderBy(rm => rm.ModelName)
.ToListAsync();
}
public async Task<RobotModelUsageInfoDto> GetUsageInfoAsync(Guid id)
{
var robotModel = await _context.RobotModels
.AsNoTracking()
.Include(rm => rm.Robots)
.FirstOrDefaultAsync(rm => rm.Id == id);
return robotModel == null
? throw new KeyNotFoundException($"Robot model with ID {id} not found.")
: new RobotModelUsageInfoDto
{
Id = robotModel.Id,
ModelName = robotModel.ModelName,
RobotCount = robotModel.Robots.Count
};
}
}