diff --git a/src/Workflow.Api/Endpoints/Form/CreateFormComponentEndpoint.cs b/src/Workflow.Api/Endpoints/Form/CreateFormComponentEndpoint.cs new file mode 100644 index 0000000..779b54c --- /dev/null +++ b/src/Workflow.Api/Endpoints/Form/CreateFormComponentEndpoint.cs @@ -0,0 +1,42 @@ +using FastEndpoints; +using MediatR; +using Workflow.Application.Form.ComponentRegistry.Commands; +using Workflow.Application.Form.ComponentRegistry.DTOs; + +namespace Workflow.Api.Endpoints.Form; + +public class CreateFormComponentEndpoint : Endpoint +{ + private readonly IMediator _mediator; + + public CreateFormComponentEndpoint(IMediator mediator) => _mediator = mediator; + + public override void Configure() + { + Post("/form-components"); + AllowAnonymous(); + Summary(s => + { + s.Summary = "创建组件注册"; + }); + } + + public override async Task HandleAsync(CreateFormComponentRequest req, CancellationToken ct) + { + var command = new CreateFormComponentCommand( + req.Name, req.DisplayName, req.Category, req.Icon, + req.DefaultSchema, req.SupportedProps); + var result = await _mediator.Send(command, ct); + await Send.ResponseAsync(result, 201, ct); + } +} + +public class CreateFormComponentRequest +{ + public string Name { get; set; } = string.Empty; + public string DisplayName { get; set; } = string.Empty; + public string Category { get; set; } = string.Empty; + public string Icon { get; set; } = string.Empty; + public string DefaultSchema { get; set; } = string.Empty; + public string SupportedProps { get; set; } = "{}"; +} diff --git a/src/Workflow.Api/Endpoints/Form/DeleteFormComponentEndpoint.cs b/src/Workflow.Api/Endpoints/Form/DeleteFormComponentEndpoint.cs new file mode 100644 index 0000000..1657557 --- /dev/null +++ b/src/Workflow.Api/Endpoints/Form/DeleteFormComponentEndpoint.cs @@ -0,0 +1,36 @@ +using FastEndpoints; +using MediatR; +using Workflow.Application.Form.ComponentRegistry.Commands; + +namespace Workflow.Api.Endpoints.Form; + +public class DeleteFormComponentEndpoint : Endpoint +{ + private readonly IMediator _mediator; + + public DeleteFormComponentEndpoint(IMediator mediator) => _mediator = mediator; + + public override void Configure() + { + Delete("/form-components/{Id}"); + AllowAnonymous(); + Summary(s => + { + s.Summary = "删除组件注册(软删除)"; + }); + } + + public override async Task HandleAsync(DeleteFormComponentRequest req, CancellationToken ct) + { + var command = new DeleteFormComponentCommand(req.Id); + await _mediator.Send(command, ct); + HttpContext.Response.StatusCode = 200; + HttpContext.Response.ContentType = "application/json"; + await HttpContext.Response.WriteAsync("{}", ct); + } +} + +public class DeleteFormComponentRequest +{ + public Guid Id { get; set; } +} diff --git a/src/Workflow.Api/Endpoints/Form/GetFormComponentByIdEndpoint.cs b/src/Workflow.Api/Endpoints/Form/GetFormComponentByIdEndpoint.cs new file mode 100644 index 0000000..8e954db --- /dev/null +++ b/src/Workflow.Api/Endpoints/Form/GetFormComponentByIdEndpoint.cs @@ -0,0 +1,34 @@ +using FastEndpoints; +using MediatR; +using Workflow.Application.Form.ComponentRegistry.DTOs; +using Workflow.Application.Form.ComponentRegistry.Queries; + +namespace Workflow.Api.Endpoints.Form; + +public class GetFormComponentByIdEndpoint : Endpoint +{ + private readonly IMediator _mediator; + + public GetFormComponentByIdEndpoint(IMediator mediator) => _mediator = mediator; + + public override void Configure() + { + Get("/form-components/{Id}"); + AllowAnonymous(); + Summary(s => + { + s.Summary = "根据ID获取组件注册详情"; + }); + } + + public override async Task HandleAsync(GetFormComponentByIdRequest req, CancellationToken ct) + { + var result = await _mediator.Send(new GetFormComponentByIdQuery(req.Id), ct); + await Send.OkAsync(result, ct); + } +} + +public class GetFormComponentByIdRequest +{ + public Guid Id { get; set; } +} diff --git a/src/Workflow.Api/Endpoints/Form/GetFormComponentsEndpoint.cs b/src/Workflow.Api/Endpoints/Form/GetFormComponentsEndpoint.cs new file mode 100644 index 0000000..c7f9ab3 --- /dev/null +++ b/src/Workflow.Api/Endpoints/Form/GetFormComponentsEndpoint.cs @@ -0,0 +1,31 @@ +using FastEndpoints; +using MediatR; +using Workflow.Application.Form.ComponentRegistry.DTOs; +using Workflow.Application.Form.ComponentRegistry.Queries; + +namespace Workflow.Api.Endpoints.Form; + +public class GetFormComponentsEndpoint : EndpointWithoutRequest> +{ + private readonly IMediator _mediator; + + public GetFormComponentsEndpoint(IMediator mediator) => _mediator = mediator; + + public override void Configure() + { + Get("/form-components"); + AllowAnonymous(); + Summary(s => + { + s.Summary = "获取组件注册列表"; + }); + } + + public override async Task HandleAsync(CancellationToken ct) + { + var category = Query("category", isRequired: false); + var isActive = Query("isActive", isRequired: false); + var result = await _mediator.Send(new GetFormComponentsQuery(category, isActive), ct); + await Send.OkAsync(result, ct); + } +} diff --git a/src/Workflow.Api/Endpoints/Form/UpdateFormComponentEndpoint.cs b/src/Workflow.Api/Endpoints/Form/UpdateFormComponentEndpoint.cs new file mode 100644 index 0000000..2bc876f --- /dev/null +++ b/src/Workflow.Api/Endpoints/Form/UpdateFormComponentEndpoint.cs @@ -0,0 +1,43 @@ +using FastEndpoints; +using MediatR; +using Workflow.Application.Form.ComponentRegistry.Commands; +using Workflow.Application.Form.ComponentRegistry.DTOs; + +namespace Workflow.Api.Endpoints.Form; + +public class UpdateFormComponentEndpoint : Endpoint +{ + private readonly IMediator _mediator; + + public UpdateFormComponentEndpoint(IMediator mediator) => _mediator = mediator; + + public override void Configure() + { + Put("/form-components/{Id}"); + AllowAnonymous(); + Summary(s => + { + s.Summary = "更新组件注册"; + }); + } + + public override async Task HandleAsync(UpdateFormComponentRequest req, CancellationToken ct) + { + var command = new UpdateFormComponentCommand( + req.Id, req.DisplayName, req.Category, req.Icon, + req.DefaultSchema, req.SupportedProps, req.IsActive); + var result = await _mediator.Send(command, ct); + await Send.OkAsync(result, ct); + } +} + +public class UpdateFormComponentRequest +{ + public Guid Id { get; set; } + public string DisplayName { get; set; } = string.Empty; + public string Category { get; set; } = string.Empty; + public string Icon { get; set; } = string.Empty; + public string DefaultSchema { get; set; } = string.Empty; + public string SupportedProps { get; set; } = "{}"; + public bool IsActive { get; set; } = true; +} diff --git a/src/Workflow.Application/Form/ComponentRegistry/Commands/CreateFormComponentCommand.cs b/src/Workflow.Application/Form/ComponentRegistry/Commands/CreateFormComponentCommand.cs new file mode 100644 index 0000000..4f0b5e5 --- /dev/null +++ b/src/Workflow.Application/Form/ComponentRegistry/Commands/CreateFormComponentCommand.cs @@ -0,0 +1,56 @@ +using MediatR; +using Microsoft.EntityFrameworkCore; +using Workflow.Application.Form.ComponentRegistry.DTOs; +using Workflow.Domain.Exceptions; +using Workflow.Infrastructure.Persistence; + +namespace Workflow.Application.Form.ComponentRegistry.Commands; + +public record CreateFormComponentCommand( + string Name, + string DisplayName, + string Category, + string Icon, + string DefaultSchema, + string SupportedProps +) : IRequest; + +public class CreateFormComponentCommandHandler(WorkflowDbContext db) + : IRequestHandler +{ + public async Task Handle(CreateFormComponentCommand request, CancellationToken ct) + { + if (await db.FormComponentRegistries.AnyAsync(c => c.Name == request.Name, ct)) + throw new BusinessException($"组件 {request.Name} 已存在"); + + var entity = new Domain.Entities.FormComponentRegistry + { + Id = Guid.NewGuid(), + Name = request.Name, + DisplayName = request.DisplayName, + Category = request.Category, + Icon = request.Icon, + DefaultSchema = request.DefaultSchema, + SupportedProps = request.SupportedProps, + SortOrder = 100, + }; + + db.FormComponentRegistries.Add(entity); + await db.SaveChangesAsync(ct); + + return MapToDto(entity); + } + + private static FormComponentDto MapToDto(Domain.Entities.FormComponentRegistry e) => new() + { + Id = e.Id, + Name = e.Name, + DisplayName = e.DisplayName, + Category = e.Category, + Icon = e.Icon, + DefaultSchema = e.DefaultSchema, + SupportedProps = e.SupportedProps, + IsActive = e.IsActive, + SortOrder = e.SortOrder, + }; +} diff --git a/src/Workflow.Application/Form/ComponentRegistry/Commands/DeleteFormComponentCommand.cs b/src/Workflow.Application/Form/ComponentRegistry/Commands/DeleteFormComponentCommand.cs new file mode 100644 index 0000000..4ddf43a --- /dev/null +++ b/src/Workflow.Application/Form/ComponentRegistry/Commands/DeleteFormComponentCommand.cs @@ -0,0 +1,25 @@ +using MediatR; +using Microsoft.EntityFrameworkCore; +using Workflow.Domain.Exceptions; +using Workflow.Infrastructure.Persistence; + +namespace Workflow.Application.Form.ComponentRegistry.Commands; + +public record DeleteFormComponentCommand(Guid Id) : IRequest; + +public class DeleteFormComponentCommandHandler(WorkflowDbContext db) + : IRequestHandler +{ + public async Task Handle(DeleteFormComponentCommand request, CancellationToken ct) + { + var entity = await db.FormComponentRegistries.FindAsync([request.Id], ct) + ?? throw new NotFoundException("组件不存在"); + + entity.IsDeleted = true; + entity.UpdatedAt = DateTime.UtcNow; + + await db.SaveChangesAsync(ct); + + return Unit.Value; + } +} diff --git a/src/Workflow.Application/Form/ComponentRegistry/Commands/UpdateFormComponentCommand.cs b/src/Workflow.Application/Form/ComponentRegistry/Commands/UpdateFormComponentCommand.cs new file mode 100644 index 0000000..1bede50 --- /dev/null +++ b/src/Workflow.Application/Form/ComponentRegistry/Commands/UpdateFormComponentCommand.cs @@ -0,0 +1,52 @@ +using MediatR; +using Microsoft.EntityFrameworkCore; +using Workflow.Application.Form.ComponentRegistry.DTOs; +using Workflow.Domain.Exceptions; +using Workflow.Infrastructure.Persistence; + +namespace Workflow.Application.Form.ComponentRegistry.Commands; + +public record UpdateFormComponentCommand( + Guid Id, + string DisplayName, + string Category, + string Icon, + string DefaultSchema, + string SupportedProps, + bool IsActive +) : IRequest; + +public class UpdateFormComponentCommandHandler(WorkflowDbContext db) + : IRequestHandler +{ + public async Task Handle(UpdateFormComponentCommand request, CancellationToken ct) + { + var entity = await db.FormComponentRegistries.FindAsync([request.Id], ct) + ?? throw new NotFoundException("组件不存在"); + + entity.DisplayName = request.DisplayName; + entity.Category = request.Category; + entity.Icon = request.Icon; + entity.DefaultSchema = request.DefaultSchema; + entity.SupportedProps = request.SupportedProps; + entity.IsActive = request.IsActive; + entity.UpdatedAt = DateTime.UtcNow; + + await db.SaveChangesAsync(ct); + + return MapToDto(entity); + } + + private static FormComponentDto MapToDto(Domain.Entities.FormComponentRegistry e) => new() + { + Id = e.Id, + Name = e.Name, + DisplayName = e.DisplayName, + Category = e.Category, + Icon = e.Icon, + DefaultSchema = e.DefaultSchema, + SupportedProps = e.SupportedProps, + IsActive = e.IsActive, + SortOrder = e.SortOrder, + }; +} diff --git a/src/Workflow.Application/Form/ComponentRegistry/DTOs/FormComponentDto.cs b/src/Workflow.Application/Form/ComponentRegistry/DTOs/FormComponentDto.cs new file mode 100644 index 0000000..d326906 --- /dev/null +++ b/src/Workflow.Application/Form/ComponentRegistry/DTOs/FormComponentDto.cs @@ -0,0 +1,14 @@ +namespace Workflow.Application.Form.ComponentRegistry.DTOs; + +public class FormComponentDto +{ + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + public string DisplayName { get; set; } = string.Empty; + public string Category { get; set; } = string.Empty; + public string Icon { get; set; } = string.Empty; + public string DefaultSchema { get; set; } = string.Empty; + public string SupportedProps { get; set; } = "{}"; + public bool IsActive { get; set; } + public int SortOrder { get; set; } +} diff --git a/src/Workflow.Application/Form/ComponentRegistry/Queries/GetFormComponentByIdQuery.cs b/src/Workflow.Application/Form/ComponentRegistry/Queries/GetFormComponentByIdQuery.cs new file mode 100644 index 0000000..33e67d2 --- /dev/null +++ b/src/Workflow.Application/Form/ComponentRegistry/Queries/GetFormComponentByIdQuery.cs @@ -0,0 +1,36 @@ +using MediatR; +using Microsoft.EntityFrameworkCore; +using Workflow.Application.Form.ComponentRegistry.DTOs; +using Workflow.Domain.Exceptions; +using Workflow.Infrastructure.Persistence; + +namespace Workflow.Application.Form.ComponentRegistry.Queries; + +public record GetFormComponentByIdQuery(Guid Id) : IRequest; + +public class GetFormComponentByIdQueryHandler(WorkflowDbContext db) + : IRequestHandler +{ + public async Task Handle(GetFormComponentByIdQuery request, CancellationToken ct) + { + var entity = await db.FormComponentRegistries + .AsNoTracking() + .FirstOrDefaultAsync(c => c.Id == request.Id, ct) + ?? throw new NotFoundException("组件不存在"); + + return MapToDto(entity); + } + + private static FormComponentDto MapToDto(Domain.Entities.FormComponentRegistry e) => new() + { + Id = e.Id, + Name = e.Name, + DisplayName = e.DisplayName, + Category = e.Category, + Icon = e.Icon, + DefaultSchema = e.DefaultSchema, + SupportedProps = e.SupportedProps, + IsActive = e.IsActive, + SortOrder = e.SortOrder, + }; +} diff --git a/src/Workflow.Application/Form/ComponentRegistry/Queries/GetFormComponentsQuery.cs b/src/Workflow.Application/Form/ComponentRegistry/Queries/GetFormComponentsQuery.cs new file mode 100644 index 0000000..0efe51d --- /dev/null +++ b/src/Workflow.Application/Form/ComponentRegistry/Queries/GetFormComponentsQuery.cs @@ -0,0 +1,45 @@ +using MediatR; +using Microsoft.EntityFrameworkCore; +using Workflow.Application.Form.ComponentRegistry.DTOs; +using Workflow.Infrastructure.Persistence; + +namespace Workflow.Application.Form.ComponentRegistry.Queries; + +public record GetFormComponentsQuery( + string? Category, + bool? IsActive +) : IRequest>; + +public class GetFormComponentsQueryHandler(WorkflowDbContext db) + : IRequestHandler> +{ + public async Task> Handle(GetFormComponentsQuery request, CancellationToken ct) + { + var query = db.FormComponentRegistries.AsNoTracking(); + + if (!string.IsNullOrWhiteSpace(request.Category)) + query = query.Where(c => c.Category == request.Category); + + if (request.IsActive.HasValue) + query = query.Where(c => c.IsActive == request.IsActive.Value); + + var entities = await query + .OrderBy(c => c.SortOrder) + .ToListAsync(ct); + + return entities.Select(MapToDto).ToList(); + } + + private static FormComponentDto MapToDto(Domain.Entities.FormComponentRegistry e) => new() + { + Id = e.Id, + Name = e.Name, + DisplayName = e.DisplayName, + Category = e.Category, + Icon = e.Icon, + DefaultSchema = e.DefaultSchema, + SupportedProps = e.SupportedProps, + IsActive = e.IsActive, + SortOrder = e.SortOrder, + }; +}