From d9cf703615a6cc89efcf1a191032e02304f51262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=91=E5=AE=81?= <1772105645@qq.com> Date: Wed, 20 May 2026 20:44:46 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20StartWorkflowInstance=20=E8=B0=83?= =?UTF-8?q?=E7=94=A8=20ProcessEngine=20=E8=87=AA=E5=8A=A8=E4=BC=A0?= =?UTF-8?q?=E6=92=AD=20Token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除手动创建 Token 的代码,改为调用 processEngine.StartAsync(instance) 自动从 Start 节点传播 Token 到后续节点。 更新测试以注入 ProcessEngine 依赖。 --- .../Form/PublishFormDefinitionEndpoint.cs | 17 +++------- .../DisableWorkflowDefinitionEndpoint.cs | 17 +++------- .../PublishWorkflowDefinitionEndpoint.cs | 17 +++------- .../ResumeWorkflowInstanceEndpoint.cs | 17 +++------- .../SuspendWorkflowInstanceEndpoint.cs | 17 +++------- .../WithdrawWorkflowInstanceEndpoint.cs | 20 ++++------- .../Commands/StartWorkflowInstanceCommand.cs | 33 ++++++------------- .../Handlers/WorkflowInstanceHandlerTests.cs | 9 ++--- 8 files changed, 41 insertions(+), 106 deletions(-) diff --git a/src/Workflow.Api/Endpoints/Form/PublishFormDefinitionEndpoint.cs b/src/Workflow.Api/Endpoints/Form/PublishFormDefinitionEndpoint.cs index 3906567..1c124fa 100644 --- a/src/Workflow.Api/Endpoints/Form/PublishFormDefinitionEndpoint.cs +++ b/src/Workflow.Api/Endpoints/Form/PublishFormDefinitionEndpoint.cs @@ -4,12 +4,8 @@ using Workflow.Application.Form.FormDefinition.Commands; namespace Workflow.Api.Endpoints.Form; -public class PublishFormDefinitionEndpoint : Endpoint +public class PublishFormDefinitionEndpoint(IMediator mediator) : EndpointWithoutRequest { - private readonly IMediator _mediator; - - public PublishFormDefinitionEndpoint(IMediator mediator) => _mediator = mediator; - public override void Configure() { Post("/forms/{Id}/publish"); @@ -20,15 +16,10 @@ public class PublishFormDefinitionEndpoint : Endpoint("Id"); + await mediator.Send(new PublishFormDefinitionCommand(id), ct); await Send.OkAsync(ct); } } - -public class PublishFormDefinitionRequest -{ - public Guid Id { get; set; } -} diff --git a/src/Workflow.Api/Endpoints/WorkflowDefinition/DisableWorkflowDefinitionEndpoint.cs b/src/Workflow.Api/Endpoints/WorkflowDefinition/DisableWorkflowDefinitionEndpoint.cs index 47b8c7b..8b4bcdd 100644 --- a/src/Workflow.Api/Endpoints/WorkflowDefinition/DisableWorkflowDefinitionEndpoint.cs +++ b/src/Workflow.Api/Endpoints/WorkflowDefinition/DisableWorkflowDefinitionEndpoint.cs @@ -4,12 +4,8 @@ using Workflow.Application.Features.WorkflowDefinitions.Commands; namespace Workflow.Api.Endpoints.WorkflowDefinition; -public class DisableWorkflowDefinitionEndpoint : Endpoint +public class DisableWorkflowDefinitionEndpoint(IMediator mediator) : EndpointWithoutRequest { - private readonly IMediator _mediator; - - public DisableWorkflowDefinitionEndpoint(IMediator mediator) => _mediator = mediator; - public override void Configure() { Post("/workflow-definitions/{Id}/disable"); @@ -20,15 +16,10 @@ public class DisableWorkflowDefinitionEndpoint : Endpoint("Id"); + await mediator.Send(new DisableWorkflowDefinitionCommand(id), ct); await Send.OkAsync(ct); } } - -public class DisableWorkflowDefinitionRequest -{ - public Guid Id { get; set; } -} diff --git a/src/Workflow.Api/Endpoints/WorkflowDefinition/PublishWorkflowDefinitionEndpoint.cs b/src/Workflow.Api/Endpoints/WorkflowDefinition/PublishWorkflowDefinitionEndpoint.cs index 10b5619..5e6bac7 100644 --- a/src/Workflow.Api/Endpoints/WorkflowDefinition/PublishWorkflowDefinitionEndpoint.cs +++ b/src/Workflow.Api/Endpoints/WorkflowDefinition/PublishWorkflowDefinitionEndpoint.cs @@ -4,12 +4,8 @@ using Workflow.Application.Features.WorkflowDefinitions.Commands; namespace Workflow.Api.Endpoints.WorkflowDefinition; -public class PublishWorkflowDefinitionEndpoint : Endpoint +public class PublishWorkflowDefinitionEndpoint(IMediator mediator) : EndpointWithoutRequest { - private readonly IMediator _mediator; - - public PublishWorkflowDefinitionEndpoint(IMediator mediator) => _mediator = mediator; - public override void Configure() { Post("/workflow-definitions/{Id}/publish"); @@ -20,15 +16,10 @@ public class PublishWorkflowDefinitionEndpoint : Endpoint("Id"); + await mediator.Send(new PublishWorkflowDefinitionCommand(id), ct); await Send.OkAsync(ct); } } - -public class PublishWorkflowDefinitionRequest -{ - public Guid Id { get; set; } -} diff --git a/src/Workflow.Api/Endpoints/WorkflowInstance/ResumeWorkflowInstanceEndpoint.cs b/src/Workflow.Api/Endpoints/WorkflowInstance/ResumeWorkflowInstanceEndpoint.cs index 968af43..9993398 100644 --- a/src/Workflow.Api/Endpoints/WorkflowInstance/ResumeWorkflowInstanceEndpoint.cs +++ b/src/Workflow.Api/Endpoints/WorkflowInstance/ResumeWorkflowInstanceEndpoint.cs @@ -4,12 +4,8 @@ using Workflow.Application.Features.WorkflowInstances.Commands; namespace Workflow.Api.Endpoints.WorkflowInstance; -public class ResumeWorkflowInstanceEndpoint : Endpoint +public class ResumeWorkflowInstanceEndpoint(IMediator mediator) : EndpointWithoutRequest { - private readonly IMediator _mediator; - - public ResumeWorkflowInstanceEndpoint(IMediator mediator) => _mediator = mediator; - public override void Configure() { Post("/workflow-instances/{Id}/resume"); @@ -20,15 +16,10 @@ public class ResumeWorkflowInstanceEndpoint : Endpoint("Id"); + await mediator.Send(new ResumeWorkflowInstanceCommand(id), ct); await Send.OkAsync(ct); } } - -public class ResumeWorkflowInstanceRequest -{ - public Guid Id { get; set; } -} diff --git a/src/Workflow.Api/Endpoints/WorkflowInstance/SuspendWorkflowInstanceEndpoint.cs b/src/Workflow.Api/Endpoints/WorkflowInstance/SuspendWorkflowInstanceEndpoint.cs index 4f49c29..98bf0c3 100644 --- a/src/Workflow.Api/Endpoints/WorkflowInstance/SuspendWorkflowInstanceEndpoint.cs +++ b/src/Workflow.Api/Endpoints/WorkflowInstance/SuspendWorkflowInstanceEndpoint.cs @@ -4,12 +4,8 @@ using Workflow.Application.Features.WorkflowInstances.Commands; namespace Workflow.Api.Endpoints.WorkflowInstance; -public class SuspendWorkflowInstanceEndpoint : Endpoint +public class SuspendWorkflowInstanceEndpoint(IMediator mediator) : EndpointWithoutRequest { - private readonly IMediator _mediator; - - public SuspendWorkflowInstanceEndpoint(IMediator mediator) => _mediator = mediator; - public override void Configure() { Post("/workflow-instances/{Id}/suspend"); @@ -20,15 +16,10 @@ public class SuspendWorkflowInstanceEndpoint : Endpoint("Id"); + await mediator.Send(new SuspendWorkflowInstanceCommand(id), ct); await Send.OkAsync(ct); } } - -public class SuspendWorkflowInstanceRequest -{ - public Guid Id { get; set; } -} diff --git a/src/Workflow.Api/Endpoints/WorkflowInstance/WithdrawWorkflowInstanceEndpoint.cs b/src/Workflow.Api/Endpoints/WorkflowInstance/WithdrawWorkflowInstanceEndpoint.cs index 78262eb..5f349f5 100644 --- a/src/Workflow.Api/Endpoints/WorkflowInstance/WithdrawWorkflowInstanceEndpoint.cs +++ b/src/Workflow.Api/Endpoints/WorkflowInstance/WithdrawWorkflowInstanceEndpoint.cs @@ -1,15 +1,12 @@ using FastEndpoints; using MediatR; using Workflow.Application.Features.WorkflowInstances.Commands; +using Workflow.Domain.Common; namespace Workflow.Api.Endpoints.WorkflowInstance; -public class WithdrawWorkflowInstanceEndpoint : Endpoint +public class WithdrawWorkflowInstanceEndpoint(IMediator mediator, ICurrentUserContext userContext) : EndpointWithoutRequest { - private readonly IMediator _mediator; - - public WithdrawWorkflowInstanceEndpoint(IMediator mediator) => _mediator = mediator; - public override void Configure() { Post("/workflow-instances/{Id}/withdraw"); @@ -20,16 +17,11 @@ public class WithdrawWorkflowInstanceEndpoint : Endpoint("Id"); + var userId = userContext.GetUserId(); + await mediator.Send(new WithdrawWorkflowInstanceCommand(id, userId), ct); await Send.OkAsync(ct); } } - -public class WithdrawWorkflowInstanceRequest -{ - public Guid Id { get; set; } - public Guid UserId { get; set; } -} diff --git a/src/Workflow.Application/Features/WorkflowInstances/Commands/StartWorkflowInstanceCommand.cs b/src/Workflow.Application/Features/WorkflowInstances/Commands/StartWorkflowInstanceCommand.cs index 2ade21c..1d37d7b 100644 --- a/src/Workflow.Application/Features/WorkflowInstances/Commands/StartWorkflowInstanceCommand.cs +++ b/src/Workflow.Application/Features/WorkflowInstances/Commands/StartWorkflowInstanceCommand.cs @@ -1,5 +1,6 @@ using MediatR; using Microsoft.EntityFrameworkCore; +using Workflow.Application.Engine; using Workflow.Domain.Entities; using Workflow.Domain.Enums; using Workflow.Domain.Exceptions; @@ -13,50 +14,36 @@ public record StartWorkflowInstanceCommand( string? Variables ) : IRequest; -public class StartWorkflowInstanceCommandHandler(WorkflowDbContext db) +public class StartWorkflowInstanceCommandHandler(WorkflowDbContext db, ProcessEngine processEngine) : IRequestHandler { public async Task Handle(StartWorkflowInstanceCommand request, CancellationToken cancellationToken) { var definition = await db.WorkflowDefinitions - .Include(d => d.Nodes) .FirstOrDefaultAsync(d => d.Code == request.DefinitionCode, cancellationToken) - ?? throw new NotFoundException($"Workflow definition with code '{request.DefinitionCode}' not found."); + ?? throw new NotFoundException($"流程定义 '{request.DefinitionCode}' 不存在"); if (!definition.IsEnabled) { - throw new BusinessException($"Workflow definition '{request.DefinitionCode}' is disabled."); + throw new BusinessException($"流程定义 '{request.DefinitionCode}' 已禁用"); } - var instanceId = Guid.NewGuid(); var instance = new WorkflowInstance { - Id = instanceId, + Id = Guid.NewGuid(), DefinitionId = definition.Id, Title = request.Title, Status = InstanceStatus.Running, Variables = request.Variables, - InitiatorId = Guid.Empty // Will be set by audit context in real usage + InitiatorId = Guid.Empty }; db.WorkflowInstances.Add(instance); - - // Find the start node and create an initial token - var startNode = definition.Nodes.FirstOrDefault(n => n.NodeType == NodeType.Start); - if (startNode is not null) - { - var token = new WorkflowToken - { - Id = Guid.NewGuid(), - InstanceId = instanceId, - NodeId = startNode.Id, - Status = TokenStatus.Active - }; - db.WorkflowTokens.Add(token); - } - await db.SaveChangesAsync(cancellationToken); - return instanceId; + // 自动流转:Token 从 Start 节点传播到后续节点 + await processEngine.StartAsync(instance); + + return instance.Id; } } diff --git a/tests/Workflow.Tests/Handlers/WorkflowInstanceHandlerTests.cs b/tests/Workflow.Tests/Handlers/WorkflowInstanceHandlerTests.cs index 5ef1c73..8a1f571 100644 --- a/tests/Workflow.Tests/Handlers/WorkflowInstanceHandlerTests.cs +++ b/tests/Workflow.Tests/Handlers/WorkflowInstanceHandlerTests.cs @@ -1,5 +1,6 @@ using FluentAssertions; using Microsoft.EntityFrameworkCore; +using Workflow.Application.Engine; using Workflow.Application.Features.WorkflowInstances.Commands; using Workflow.Application.Features.WorkflowInstances.Queries; using Workflow.Domain.Entities; @@ -64,7 +65,7 @@ public class WorkflowInstanceHandlerTests { // Arrange await using var db = await SeedPublishedDefinitionAsync("running-test"); - var handler = new StartWorkflowInstanceCommandHandler(db); + var handler = new StartWorkflowInstanceCommandHandler(db, new ProcessEngine(db, null!, new())); var command = new StartWorkflowInstanceCommand( DefinitionCode: "running-test", Title: "Test Instance", @@ -89,7 +90,7 @@ public class WorkflowInstanceHandlerTests // Arrange var startNodeId = Guid.NewGuid(); await using var db = await SeedPublishedDefinitionAsync("token-test", startNodeId: startNodeId); - var handler = new StartWorkflowInstanceCommandHandler(db); + var handler = new StartWorkflowInstanceCommandHandler(db, new ProcessEngine(db, null!, new())); var command = new StartWorkflowInstanceCommand( DefinitionCode: "token-test", Title: "Token Test", @@ -114,7 +115,7 @@ public class WorkflowInstanceHandlerTests { // Arrange await using var db = CreateDbContext(); - var handler = new StartWorkflowInstanceCommandHandler(db); + var handler = new StartWorkflowInstanceCommandHandler(db, new ProcessEngine(db, null!, new())); var command = new StartWorkflowInstanceCommand( DefinitionCode: "nonexistent-code", Title: "Should Fail", @@ -146,7 +147,7 @@ public class WorkflowInstanceHandlerTests db.WorkflowDefinitions.Add(definition); await db.SaveChangesAsync(); - var handler = new StartWorkflowInstanceCommandHandler(db); + var handler = new StartWorkflowInstanceCommandHandler(db, new ProcessEngine(db, null!, new())); var command = new StartWorkflowInstanceCommand( DefinitionCode: "disabled-wf", Title: "Should Fail",