CreateOrUpdateTaskTemplateCommandHandler.cs 8.45 KB
using MassTransit;
using Microsoft.Extensions.Logging;
using Rcs.Application.Common;
using Rcs.Application.Dtos;
using Rcs.Application.MessageBus.Commands;
using Rcs.Domain.Entities;
using Rcs.Domain.Enums;
using Rcs.Domain.Repositories;

namespace Rcs.Infrastructure.MessageBus.Handlers.Commands;

public class CreateOrUpdateTaskTemplateCommandHandler : IConsumer<CreateOrUpdateTaskTemplateCommand>
{
    private readonly ILogger<CreateOrUpdateTaskTemplateCommandHandler> _logger;
    private readonly ITaskTemplateRepository _taskTemplateRepository;

    public CreateOrUpdateTaskTemplateCommandHandler(
        ILogger<CreateOrUpdateTaskTemplateCommandHandler> logger,
        ITaskTemplateRepository taskTemplateRepository)
    {
        _logger = logger;
        _taskTemplateRepository = taskTemplateRepository;
    }

    public async Task Consume(ConsumeContext<CreateOrUpdateTaskTemplateCommand> context)
    {
        var command = context.Message;

        try
        {
            if (!Enum.IsDefined(typeof(RobotType), command.RobotType))
            {
                throw new InvalidOperationException($"Invalid robot type: {command.RobotType}");
            }

            var robotType = (RobotType)command.RobotType;
            var normalizedManufacturer = NormalizeManufacturer(command.Manufacturer);
            var isUpdate = Guid.TryParse(command.TemplateId, out var templateId);

            if (command.IsDefault)
            {
                Guid? excludeId = isUpdate ? templateId : null;
                var existingDefault = await _taskTemplateRepository.GetDefaultTemplateAsync(
                    robotType,
                    normalizedManufacturer,
                    excludeId,
                    context.CancellationToken);

                if (existingDefault != null)
                {
                    throw new InvalidOperationException(
                        $"Default template already exists for robotType={robotType}, manufacturer={normalizedManufacturer ?? "null"}. Existing={existingDefault.TemplateName}");
                }
            }

            if (!isUpdate)
            {
                await CreateTemplateAsync(command, robotType, normalizedManufacturer, context.CancellationToken);
            }
            else
            {
                await UpdateTemplateAsync(command, templateId, robotType, normalizedManufacturer, context.CancellationToken);
            }

            await context.RespondAsync(ApiResponse.Successful());
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Create or update task template failed");
            await context.RespondAsync(ApiResponse.Failed(ex.Message));
        }
    }

    private async Task CreateTemplateAsync(
        CreateOrUpdateTaskTemplateCommand command,
        RobotType robotType,
        string? manufacturer,
        CancellationToken cancellationToken)
    {
        var existingTemplate = await _taskTemplateRepository.GetByTemplateCodeAsync(
            command.TemplateCode,
            cancellationToken);

        if (existingTemplate != null)
        {
            throw new InvalidOperationException($"TemplateCode already exists: {command.TemplateCode}");
        }

        var template = TaskTemplate.Create(
            command.TemplateCode,
            command.TemplateName,
            robotType,
            command.Description,
            manufacturer,
            command.IsEnabled,
            command.IsDefault);

        BuildTemplateSteps(template, command.TaskSteps);

        await _taskTemplateRepository.AddAsync(template, cancellationToken);
    }

    private async Task UpdateTemplateAsync(
        CreateOrUpdateTaskTemplateCommand command,
        Guid templateId,
        RobotType robotType,
        string? manufacturer,
        CancellationToken cancellationToken)
    {
        var template = await _taskTemplateRepository.GetWithFullDetailsAsync(templateId, cancellationToken);
        if (template == null)
        {
            throw new InvalidOperationException($"Task template not found: {templateId}");
        }

        template.Update(
            command.TemplateCode,
            command.TemplateName,
            robotType,
            command.Description,
            manufacturer,
            command.IsEnabled,
            command.IsDefault);

        ClearTemplateSteps(template);
        BuildTemplateSteps(template, command.TaskSteps);

        await _taskTemplateRepository.UpdateAsync(template, cancellationToken);
    }

    private static string? NormalizeManufacturer(string? manufacturer)
    {
        return string.IsNullOrWhiteSpace(manufacturer) ? null : manufacturer.Trim();
    }

    private static void ClearTemplateSteps(TaskTemplate template)
    {
        foreach (var step in template.TaskSteps.ToList())
        {
            foreach (var property in step.Properties.ToList())
            {
                foreach (var action in property.Actions.ToList())
                {
                    property.Actions.Remove(action);
                }

                step.Properties.Remove(property);
            }

            template.TaskSteps.Remove(step);
        }
    }

    private static void BuildTemplateSteps(TaskTemplate template, IEnumerable<TaskStepDto>? stepDtos)
    {
        if (stepDtos == null)
        {
            return;
        }

        foreach (var stepDto in stepDtos)
        {
            var step = new TaskStep
            {
                StepId = Guid.NewGuid(),
                TemplateId = template.TemplateId,
                Type = (TaskStepType)stepDto.Type,
                StepName = stepDto.StepName,
                Description = stepDto.Description,
                Order = stepDto.Order,
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            };

            foreach (var propertyDto in stepDto.Properties ?? Enumerable.Empty<StepPropertyDto>())
            {
                var property = new StepProperty
                {
                    PropertyId = Guid.NewGuid(),
                    StepId = step.StepId,
                    PropertyType = (StepPropertyType)propertyDto.PropertyType,
                    NodeValue = (NodeValueType)propertyDto.NodeValue,
                    CreatedAt = DateTime.Now,
                    UpdatedAt = DateTime.Now
                };

                property.PreAction1Type = propertyDto.PreAction1Type.HasValue
                    ? (ActionType)propertyDto.PreAction1Type.Value
                    : null;
                property.PreNetActions1 = propertyDto.PreNetActions1?.Where(n => Guid.TryParse(n, out _)).Select(Guid.Parse).ToList();
                property.PostAction1Type = propertyDto.PostAction1Type.HasValue
                    ? (ActionType)propertyDto.PostAction1Type.Value
                    : null;
                property.PostNetActions1 = propertyDto.PostNetActions1?.Where(n => Guid.TryParse(n, out _)).Select(Guid.Parse).ToList();
                property.PreAction2Type = propertyDto.PreAction2Type.HasValue
                    ? (ActionType)propertyDto.PreAction2Type.Value
                    : null;
                property.PreNetActions2 = propertyDto.PreNetActions2?.Where(n => Guid.TryParse(n, out _)).Select(Guid.Parse).ToList();
                property.PostAction2Type = propertyDto.PostAction2Type.HasValue
                    ? (ActionType)propertyDto.PostAction2Type.Value
                    : null;
                property.PostNetActions2 = propertyDto.PostNetActions2?.Where(n => Guid.TryParse(n, out _)).Select(Guid.Parse).ToList();

                foreach (var actionDto in propertyDto.Actions ?? Enumerable.Empty<ActionDto>())
                {
                    var action = new StepAction
                    {
                        ActionId = Guid.NewGuid(),
                        PropertyId = property.PropertyId,
                        ActionConfigId = Guid.Parse(actionDto.ActionConfigId),
                        Type = Enum.Parse<ActionCategory>(actionDto.Type),
                        ActionName = actionDto.ActionName,
                        Description = actionDto.Description,
                        BlockingType = (ActionBlockType)actionDto.BlockingType,
                        Order = actionDto.Order,
                        CreatedAt = DateTime.Now,
                        UpdatedAt = DateTime.Now
                    };
                    property.Actions.Add(action);
                }

                step.Properties.Add(property);
            }

            template.TaskSteps.Add(step);
        }
    }
}