feat: add form component registry CRUD endpoints

This commit is contained in:
向宁 2026-05-25 14:12:58 +08:00
parent fa1f8dbef5
commit d704c02d18
11 changed files with 414 additions and 0 deletions

View File

@ -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<CreateFormComponentRequest, FormComponentDto>
{
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; } = "{}";
}

View File

@ -0,0 +1,36 @@
using FastEndpoints;
using MediatR;
using Workflow.Application.Form.ComponentRegistry.Commands;
namespace Workflow.Api.Endpoints.Form;
public class DeleteFormComponentEndpoint : Endpoint<DeleteFormComponentRequest>
{
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; }
}

View File

@ -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<GetFormComponentByIdRequest, FormComponentDto>
{
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; }
}

View File

@ -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<List<FormComponentDto>>
{
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<string?>("category", isRequired: false);
var isActive = Query<bool?>("isActive", isRequired: false);
var result = await _mediator.Send(new GetFormComponentsQuery(category, isActive), ct);
await Send.OkAsync(result, ct);
}
}

View File

@ -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<UpdateFormComponentRequest, FormComponentDto>
{
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;
}

View File

@ -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<FormComponentDto>;
public class CreateFormComponentCommandHandler(WorkflowDbContext db)
: IRequestHandler<CreateFormComponentCommand, FormComponentDto>
{
public async Task<FormComponentDto> 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,
};
}

View File

@ -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<Unit>;
public class DeleteFormComponentCommandHandler(WorkflowDbContext db)
: IRequestHandler<DeleteFormComponentCommand, Unit>
{
public async Task<Unit> 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;
}
}

View File

@ -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<FormComponentDto>;
public class UpdateFormComponentCommandHandler(WorkflowDbContext db)
: IRequestHandler<UpdateFormComponentCommand, FormComponentDto>
{
public async Task<FormComponentDto> 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,
};
}

View File

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

View File

@ -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<FormComponentDto>;
public class GetFormComponentByIdQueryHandler(WorkflowDbContext db)
: IRequestHandler<GetFormComponentByIdQuery, FormComponentDto>
{
public async Task<FormComponentDto> 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,
};
}

View File

@ -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<List<FormComponentDto>>;
public class GetFormComponentsQueryHandler(WorkflowDbContext db)
: IRequestHandler<GetFormComponentsQuery, List<FormComponentDto>>
{
public async Task<List<FormComponentDto>> 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,
};
}