fix: StartWorkflowInstance 调用 ProcessEngine 自动传播 Token
移除手动创建 Token 的代码,改为调用 processEngine.StartAsync(instance) 自动从 Start 节点传播 Token 到后续节点。 更新测试以注入 ProcessEngine 依赖。
This commit is contained in:
parent
fc4ecbbacc
commit
d9cf703615
@ -4,12 +4,8 @@ using Workflow.Application.Form.FormDefinition.Commands;
|
|||||||
|
|
||||||
namespace Workflow.Api.Endpoints.Form;
|
namespace Workflow.Api.Endpoints.Form;
|
||||||
|
|
||||||
public class PublishFormDefinitionEndpoint : Endpoint<PublishFormDefinitionRequest>
|
public class PublishFormDefinitionEndpoint(IMediator mediator) : EndpointWithoutRequest
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
public PublishFormDefinitionEndpoint(IMediator mediator) => _mediator = mediator;
|
|
||||||
|
|
||||||
public override void Configure()
|
public override void Configure()
|
||||||
{
|
{
|
||||||
Post("/forms/{Id}/publish");
|
Post("/forms/{Id}/publish");
|
||||||
@ -20,15 +16,10 @@ public class PublishFormDefinitionEndpoint : Endpoint<PublishFormDefinitionReque
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task HandleAsync(PublishFormDefinitionRequest req, CancellationToken ct)
|
public override async Task HandleAsync(CancellationToken ct)
|
||||||
{
|
{
|
||||||
var command = new PublishFormDefinitionCommand(req.Id);
|
var id = Route<Guid>("Id");
|
||||||
await _mediator.Send(command, ct);
|
await mediator.Send(new PublishFormDefinitionCommand(id), ct);
|
||||||
await Send.OkAsync(ct);
|
await Send.OkAsync(ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PublishFormDefinitionRequest
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
}
|
|
||||||
|
|||||||
@ -4,12 +4,8 @@ using Workflow.Application.Features.WorkflowDefinitions.Commands;
|
|||||||
|
|
||||||
namespace Workflow.Api.Endpoints.WorkflowDefinition;
|
namespace Workflow.Api.Endpoints.WorkflowDefinition;
|
||||||
|
|
||||||
public class DisableWorkflowDefinitionEndpoint : Endpoint<DisableWorkflowDefinitionRequest>
|
public class DisableWorkflowDefinitionEndpoint(IMediator mediator) : EndpointWithoutRequest
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
public DisableWorkflowDefinitionEndpoint(IMediator mediator) => _mediator = mediator;
|
|
||||||
|
|
||||||
public override void Configure()
|
public override void Configure()
|
||||||
{
|
{
|
||||||
Post("/workflow-definitions/{Id}/disable");
|
Post("/workflow-definitions/{Id}/disable");
|
||||||
@ -20,15 +16,10 @@ public class DisableWorkflowDefinitionEndpoint : Endpoint<DisableWorkflowDefinit
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task HandleAsync(DisableWorkflowDefinitionRequest req, CancellationToken ct)
|
public override async Task HandleAsync(CancellationToken ct)
|
||||||
{
|
{
|
||||||
var command = new DisableWorkflowDefinitionCommand(req.Id);
|
var id = Route<Guid>("Id");
|
||||||
await _mediator.Send(command, ct);
|
await mediator.Send(new DisableWorkflowDefinitionCommand(id), ct);
|
||||||
await Send.OkAsync(ct);
|
await Send.OkAsync(ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DisableWorkflowDefinitionRequest
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
}
|
|
||||||
|
|||||||
@ -4,12 +4,8 @@ using Workflow.Application.Features.WorkflowDefinitions.Commands;
|
|||||||
|
|
||||||
namespace Workflow.Api.Endpoints.WorkflowDefinition;
|
namespace Workflow.Api.Endpoints.WorkflowDefinition;
|
||||||
|
|
||||||
public class PublishWorkflowDefinitionEndpoint : Endpoint<PublishWorkflowDefinitionRequest>
|
public class PublishWorkflowDefinitionEndpoint(IMediator mediator) : EndpointWithoutRequest
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
public PublishWorkflowDefinitionEndpoint(IMediator mediator) => _mediator = mediator;
|
|
||||||
|
|
||||||
public override void Configure()
|
public override void Configure()
|
||||||
{
|
{
|
||||||
Post("/workflow-definitions/{Id}/publish");
|
Post("/workflow-definitions/{Id}/publish");
|
||||||
@ -20,15 +16,10 @@ public class PublishWorkflowDefinitionEndpoint : Endpoint<PublishWorkflowDefinit
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task HandleAsync(PublishWorkflowDefinitionRequest req, CancellationToken ct)
|
public override async Task HandleAsync(CancellationToken ct)
|
||||||
{
|
{
|
||||||
var command = new PublishWorkflowDefinitionCommand(req.Id);
|
var id = Route<Guid>("Id");
|
||||||
await _mediator.Send(command, ct);
|
await mediator.Send(new PublishWorkflowDefinitionCommand(id), ct);
|
||||||
await Send.OkAsync(ct);
|
await Send.OkAsync(ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PublishWorkflowDefinitionRequest
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
}
|
|
||||||
|
|||||||
@ -4,12 +4,8 @@ using Workflow.Application.Features.WorkflowInstances.Commands;
|
|||||||
|
|
||||||
namespace Workflow.Api.Endpoints.WorkflowInstance;
|
namespace Workflow.Api.Endpoints.WorkflowInstance;
|
||||||
|
|
||||||
public class ResumeWorkflowInstanceEndpoint : Endpoint<ResumeWorkflowInstanceRequest>
|
public class ResumeWorkflowInstanceEndpoint(IMediator mediator) : EndpointWithoutRequest
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
public ResumeWorkflowInstanceEndpoint(IMediator mediator) => _mediator = mediator;
|
|
||||||
|
|
||||||
public override void Configure()
|
public override void Configure()
|
||||||
{
|
{
|
||||||
Post("/workflow-instances/{Id}/resume");
|
Post("/workflow-instances/{Id}/resume");
|
||||||
@ -20,15 +16,10 @@ public class ResumeWorkflowInstanceEndpoint : Endpoint<ResumeWorkflowInstanceReq
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task HandleAsync(ResumeWorkflowInstanceRequest req, CancellationToken ct)
|
public override async Task HandleAsync(CancellationToken ct)
|
||||||
{
|
{
|
||||||
var command = new ResumeWorkflowInstanceCommand(req.Id);
|
var id = Route<Guid>("Id");
|
||||||
await _mediator.Send(command, ct);
|
await mediator.Send(new ResumeWorkflowInstanceCommand(id), ct);
|
||||||
await Send.OkAsync(ct);
|
await Send.OkAsync(ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ResumeWorkflowInstanceRequest
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
}
|
|
||||||
|
|||||||
@ -4,12 +4,8 @@ using Workflow.Application.Features.WorkflowInstances.Commands;
|
|||||||
|
|
||||||
namespace Workflow.Api.Endpoints.WorkflowInstance;
|
namespace Workflow.Api.Endpoints.WorkflowInstance;
|
||||||
|
|
||||||
public class SuspendWorkflowInstanceEndpoint : Endpoint<SuspendWorkflowInstanceRequest>
|
public class SuspendWorkflowInstanceEndpoint(IMediator mediator) : EndpointWithoutRequest
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
public SuspendWorkflowInstanceEndpoint(IMediator mediator) => _mediator = mediator;
|
|
||||||
|
|
||||||
public override void Configure()
|
public override void Configure()
|
||||||
{
|
{
|
||||||
Post("/workflow-instances/{Id}/suspend");
|
Post("/workflow-instances/{Id}/suspend");
|
||||||
@ -20,15 +16,10 @@ public class SuspendWorkflowInstanceEndpoint : Endpoint<SuspendWorkflowInstanceR
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task HandleAsync(SuspendWorkflowInstanceRequest req, CancellationToken ct)
|
public override async Task HandleAsync(CancellationToken ct)
|
||||||
{
|
{
|
||||||
var command = new SuspendWorkflowInstanceCommand(req.Id);
|
var id = Route<Guid>("Id");
|
||||||
await _mediator.Send(command, ct);
|
await mediator.Send(new SuspendWorkflowInstanceCommand(id), ct);
|
||||||
await Send.OkAsync(ct);
|
await Send.OkAsync(ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SuspendWorkflowInstanceRequest
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,15 +1,12 @@
|
|||||||
using FastEndpoints;
|
using FastEndpoints;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Workflow.Application.Features.WorkflowInstances.Commands;
|
using Workflow.Application.Features.WorkflowInstances.Commands;
|
||||||
|
using Workflow.Domain.Common;
|
||||||
|
|
||||||
namespace Workflow.Api.Endpoints.WorkflowInstance;
|
namespace Workflow.Api.Endpoints.WorkflowInstance;
|
||||||
|
|
||||||
public class WithdrawWorkflowInstanceEndpoint : Endpoint<WithdrawWorkflowInstanceRequest>
|
public class WithdrawWorkflowInstanceEndpoint(IMediator mediator, ICurrentUserContext userContext) : EndpointWithoutRequest
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
public WithdrawWorkflowInstanceEndpoint(IMediator mediator) => _mediator = mediator;
|
|
||||||
|
|
||||||
public override void Configure()
|
public override void Configure()
|
||||||
{
|
{
|
||||||
Post("/workflow-instances/{Id}/withdraw");
|
Post("/workflow-instances/{Id}/withdraw");
|
||||||
@ -20,16 +17,11 @@ public class WithdrawWorkflowInstanceEndpoint : Endpoint<WithdrawWorkflowInstanc
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task HandleAsync(WithdrawWorkflowInstanceRequest req, CancellationToken ct)
|
public override async Task HandleAsync(CancellationToken ct)
|
||||||
{
|
{
|
||||||
var command = new WithdrawWorkflowInstanceCommand(req.Id, req.UserId);
|
var id = Route<Guid>("Id");
|
||||||
await _mediator.Send(command, ct);
|
var userId = userContext.GetUserId();
|
||||||
|
await mediator.Send(new WithdrawWorkflowInstanceCommand(id, userId), ct);
|
||||||
await Send.OkAsync(ct);
|
await Send.OkAsync(ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class WithdrawWorkflowInstanceRequest
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
public Guid UserId { get; set; }
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Workflow.Application.Engine;
|
||||||
using Workflow.Domain.Entities;
|
using Workflow.Domain.Entities;
|
||||||
using Workflow.Domain.Enums;
|
using Workflow.Domain.Enums;
|
||||||
using Workflow.Domain.Exceptions;
|
using Workflow.Domain.Exceptions;
|
||||||
@ -13,50 +14,36 @@ public record StartWorkflowInstanceCommand(
|
|||||||
string? Variables
|
string? Variables
|
||||||
) : IRequest<Guid>;
|
) : IRequest<Guid>;
|
||||||
|
|
||||||
public class StartWorkflowInstanceCommandHandler(WorkflowDbContext db)
|
public class StartWorkflowInstanceCommandHandler(WorkflowDbContext db, ProcessEngine processEngine)
|
||||||
: IRequestHandler<StartWorkflowInstanceCommand, Guid>
|
: IRequestHandler<StartWorkflowInstanceCommand, Guid>
|
||||||
{
|
{
|
||||||
public async Task<Guid> Handle(StartWorkflowInstanceCommand request, CancellationToken cancellationToken)
|
public async Task<Guid> Handle(StartWorkflowInstanceCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var definition = await db.WorkflowDefinitions
|
var definition = await db.WorkflowDefinitions
|
||||||
.Include(d => d.Nodes)
|
|
||||||
.FirstOrDefaultAsync(d => d.Code == request.DefinitionCode, cancellationToken)
|
.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)
|
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
|
var instance = new WorkflowInstance
|
||||||
{
|
{
|
||||||
Id = instanceId,
|
Id = Guid.NewGuid(),
|
||||||
DefinitionId = definition.Id,
|
DefinitionId = definition.Id,
|
||||||
Title = request.Title,
|
Title = request.Title,
|
||||||
Status = InstanceStatus.Running,
|
Status = InstanceStatus.Running,
|
||||||
Variables = request.Variables,
|
Variables = request.Variables,
|
||||||
InitiatorId = Guid.Empty // Will be set by audit context in real usage
|
InitiatorId = Guid.Empty
|
||||||
};
|
};
|
||||||
|
|
||||||
db.WorkflowInstances.Add(instance);
|
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);
|
await db.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
return instanceId;
|
// 自动流转:Token 从 Start 节点传播到后续节点
|
||||||
|
await processEngine.StartAsync(instance);
|
||||||
|
|
||||||
|
return instance.Id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Workflow.Application.Engine;
|
||||||
using Workflow.Application.Features.WorkflowInstances.Commands;
|
using Workflow.Application.Features.WorkflowInstances.Commands;
|
||||||
using Workflow.Application.Features.WorkflowInstances.Queries;
|
using Workflow.Application.Features.WorkflowInstances.Queries;
|
||||||
using Workflow.Domain.Entities;
|
using Workflow.Domain.Entities;
|
||||||
@ -64,7 +65,7 @@ public class WorkflowInstanceHandlerTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
await using var db = await SeedPublishedDefinitionAsync("running-test");
|
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(
|
var command = new StartWorkflowInstanceCommand(
|
||||||
DefinitionCode: "running-test",
|
DefinitionCode: "running-test",
|
||||||
Title: "Test Instance",
|
Title: "Test Instance",
|
||||||
@ -89,7 +90,7 @@ public class WorkflowInstanceHandlerTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var startNodeId = Guid.NewGuid();
|
var startNodeId = Guid.NewGuid();
|
||||||
await using var db = await SeedPublishedDefinitionAsync("token-test", startNodeId: startNodeId);
|
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(
|
var command = new StartWorkflowInstanceCommand(
|
||||||
DefinitionCode: "token-test",
|
DefinitionCode: "token-test",
|
||||||
Title: "Token Test",
|
Title: "Token Test",
|
||||||
@ -114,7 +115,7 @@ public class WorkflowInstanceHandlerTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
await using var db = CreateDbContext();
|
await using var db = CreateDbContext();
|
||||||
var handler = new StartWorkflowInstanceCommandHandler(db);
|
var handler = new StartWorkflowInstanceCommandHandler(db, new ProcessEngine(db, null!, new()));
|
||||||
var command = new StartWorkflowInstanceCommand(
|
var command = new StartWorkflowInstanceCommand(
|
||||||
DefinitionCode: "nonexistent-code",
|
DefinitionCode: "nonexistent-code",
|
||||||
Title: "Should Fail",
|
Title: "Should Fail",
|
||||||
@ -146,7 +147,7 @@ public class WorkflowInstanceHandlerTests
|
|||||||
db.WorkflowDefinitions.Add(definition);
|
db.WorkflowDefinitions.Add(definition);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
var handler = new StartWorkflowInstanceCommandHandler(db);
|
var handler = new StartWorkflowInstanceCommandHandler(db, new ProcessEngine(db, null!, new()));
|
||||||
var command = new StartWorkflowInstanceCommand(
|
var command = new StartWorkflowInstanceCommand(
|
||||||
DefinitionCode: "disabled-wf",
|
DefinitionCode: "disabled-wf",
|
||||||
Title: "Should Fail",
|
Title: "Should Fail",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user