Initial commit
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RobotNet10.FleetManager.Data;
|
||||
using RobotNet10.FleetManager.Events;
|
||||
using RobotNet10.FleetManager.Events.Events;
|
||||
using RobotNet10.FleetManager.Shared.DTOs.Robot;
|
||||
|
||||
namespace RobotNet10.FleetManager.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service implementation for managing robots.
|
||||
/// Handles business logic for CRUD operations on robots.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This service validates business rules such as duplicate robot IDs,
|
||||
/// ensures referenced robot models exist, and provides filtering and search functionality.
|
||||
/// </remarks>
|
||||
public class RobotService(
|
||||
ApplicationDbContext context,
|
||||
Logger<RobotService> logger,
|
||||
IRobotEventBus? eventBus = null) : IRobotService
|
||||
{
|
||||
private readonly ApplicationDbContext _context = context;
|
||||
private readonly Logger<RobotService> _logger = logger;
|
||||
private readonly IRobotEventBus? _eventBus = eventBus;
|
||||
|
||||
public async Task<Robot> CreateAsync(CreateRobotRequest request)
|
||||
{
|
||||
// Check if robot ID already exists
|
||||
if (await ExistsAsync(request.RobotId))
|
||||
{
|
||||
throw new InvalidOperationException($"Robot with ID '{request.RobotId}' already exists.");
|
||||
}
|
||||
|
||||
// Verify that the model exists
|
||||
var modelExists = await _context.RobotModels.AnyAsync(rm => rm.Id == request.ModelId);
|
||||
if (!modelExists)
|
||||
{
|
||||
throw new KeyNotFoundException($"Robot model with ID {request.ModelId} not found.");
|
||||
}
|
||||
|
||||
var robot = new Robot
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
RobotId = request.RobotId,
|
||||
Name = request.Name,
|
||||
ModelId = request.ModelId,
|
||||
MapId = request.MapId,
|
||||
CreatedDate = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_context.Robots.Add(robot);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.Info($"Created robot {robot.Name} with RobotId {robot.RobotId} and ID {robot.Id}");
|
||||
return robot;
|
||||
}
|
||||
|
||||
public async Task<List<Robot>> GetAllAsync(Guid? modelId = null, Guid? mapId = null)
|
||||
{
|
||||
var query = _context.Robots.AsNoTracking().Include(r => r.Model).AsQueryable();
|
||||
|
||||
if (modelId.HasValue)
|
||||
{
|
||||
query = query.Where(r => r.ModelId == modelId.Value);
|
||||
}
|
||||
|
||||
if (mapId.HasValue)
|
||||
{
|
||||
query = query.Where(r => r.MapId == mapId.Value);
|
||||
}
|
||||
|
||||
return await query
|
||||
.OrderBy(r => r.Name)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Robot?> GetByIdAsync(Guid id)
|
||||
{
|
||||
return await _context.Robots
|
||||
.AsNoTracking()
|
||||
.Include(r => r.Model)
|
||||
.FirstOrDefaultAsync(r => r.Id == id);
|
||||
}
|
||||
|
||||
public async Task<Robot?> GetByRobotIdAsync(string robotId)
|
||||
{
|
||||
return await _context.Robots
|
||||
.AsNoTracking()
|
||||
.Include(r => r.Model)
|
||||
.FirstOrDefaultAsync(r => r.RobotId == robotId);
|
||||
}
|
||||
|
||||
public async Task<Robot> UpdateAsync(Guid id, UpdateRobotRequest request)
|
||||
{
|
||||
var robot = await _context.Robots.FindAsync(id) ?? throw new KeyNotFoundException($"Robot with ID {id} not found.");
|
||||
|
||||
// Check if robot ID is being changed and if new ID already exists
|
||||
if (request.RobotId != null && request.RobotId != robot.RobotId)
|
||||
{
|
||||
if (await ExistsAsync(request.RobotId))
|
||||
{
|
||||
throw new InvalidOperationException($"Robot with ID '{request.RobotId}' already exists.");
|
||||
}
|
||||
robot.RobotId = request.RobotId;
|
||||
}
|
||||
|
||||
if (request.Name != null)
|
||||
robot.Name = request.Name;
|
||||
|
||||
if (request.ModelId.HasValue)
|
||||
{
|
||||
// Verify that the model exists
|
||||
var modelExists = await _context.RobotModels.AnyAsync(rm => rm.Id == request.ModelId.Value);
|
||||
if (!modelExists)
|
||||
{
|
||||
throw new KeyNotFoundException($"Robot model with ID {request.ModelId.Value} not found.");
|
||||
}
|
||||
|
||||
// Check if ModelId is actually changing
|
||||
var previousModelId = robot.ModelId;
|
||||
if (previousModelId != request.ModelId.Value)
|
||||
{
|
||||
robot.ModelId = request.ModelId.Value;
|
||||
|
||||
// Publish event for cache invalidation
|
||||
if (_eventBus != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var eventData = new RobotModelIdChangedEvent
|
||||
{
|
||||
RobotId = robot.RobotId,
|
||||
PreviousModelId = previousModelId,
|
||||
NewModelId = request.ModelId.Value
|
||||
};
|
||||
|
||||
_eventBus.PublishRobotModelIdChanged(eventData);
|
||||
_logger.Debug($"Published RobotModelIdChanged event for robot {robot.RobotId} (from {previousModelId} to {request.ModelId.Value})");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"Error publishing RobotModelIdChanged event: {ex.Message}");
|
||||
// Don't fail the update if event publishing fails
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MapId: if provided (including null), update it
|
||||
// Note: In C#, nullable Guid? means: HasValue = true means a value was provided (could be Guid.Empty or a valid Guid)
|
||||
// To distinguish between "not provided" and "explicitly set to null", we'd need a different approach
|
||||
// For now, we'll only update MapId if it has a value (non-null)
|
||||
if (request.MapId.HasValue)
|
||||
{
|
||||
robot.MapId = request.MapId.Value;
|
||||
}
|
||||
|
||||
robot.UpdatedDate = DateTime.UtcNow;
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.Info($"Updated robot {robot.Name} with RobotId {robot.RobotId} and ID {robot.Id}");
|
||||
return robot;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteAsync(Guid id)
|
||||
{
|
||||
var robot = await _context.Robots.FindAsync(id);
|
||||
if (robot == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_context.Robots.Remove(robot);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.Info($"Deleted robot {robot.Name} with RobotId {robot.RobotId} and ID {robot.Id}");
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<List<Robot>> GetByModelIdAsync(Guid modelId)
|
||||
{
|
||||
return await _context.Robots
|
||||
.AsNoTracking()
|
||||
.Include(r => r.Model)
|
||||
.Where(r => r.ModelId == modelId)
|
||||
.OrderBy(r => r.Name)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<Robot>> SearchAsync(string query)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
return await GetAllAsync();
|
||||
}
|
||||
|
||||
var lowerQuery = query.ToLowerInvariant();
|
||||
return await _context.Robots
|
||||
.AsNoTracking()
|
||||
.Include(r => r.Model)
|
||||
.Where(r => r.RobotId.ToLower().Contains(lowerQuery) || r.Name.ToLower().Contains(lowerQuery))
|
||||
.OrderBy(r => r.Name)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(string robotId)
|
||||
{
|
||||
return await _context.Robots
|
||||
.AnyAsync(r => r.RobotId == robotId);
|
||||
}
|
||||
|
||||
public async Task<List<Robot>> GetByModelNameAsync(string modelName)
|
||||
{
|
||||
return await _context.Robots
|
||||
.AsNoTracking()
|
||||
.Include(r => r.Model)
|
||||
.Where(r => r.Name == modelName)
|
||||
.OrderBy(r => r.Name)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user