Skip to Content

User Tasks

Der UserTaskClient ermöglicht das Abfragen und Bearbeiten von User Tasks aus BPMN-Prozessen.

Client erstellen

using ProcessCube.Engine; var engineAddress = "http://localhost:8000"; var client = ClientFactory.CreateUserTaskClient(engineAddress);

User Tasks abfragen

QueryAsync()

Fragt User Tasks mit Filtern ab:

using ProcessCube.Engine; using ProcessCube.Engine.UserTasks; var engineAddress = "http://localhost:8000"; var client = ClientFactory.CreateUserTaskClient(engineAddress); // Alle wartenden User Tasks abrufen var userTasks = await client.QueryAsync(query => { query.FilterByState(UserTaskState.Suspended); }); Console.WriteLine($"Offene User Tasks: {userTasks.Count}"); foreach (var task in userTasks) { Console.WriteLine($" Task: {task.Name}"); Console.WriteLine($" ID: {task.Id}"); Console.WriteLine($" Prozess: {task.ProcessModelId}"); Console.WriteLine($" Form Fields: {task.Configuration.FormFields.Count}"); }

Filter-Optionen

Nach ProcessInstanceId

var userTasks = await client.QueryAsync(query => { query.FilterByProcessInstanceId("abc-123-def-456"); });

Nach ProcessModelId

var userTasks = await client.QueryAsync(query => { query.FilterByProcessModelId("ApprovalProcess"); query.FilterByState(UserTaskState.Suspended); });

Nach UserTaskId

var userTasks = await client.QueryAsync(query => { query.FilterByUserTaskId("UserTask_Approve"); });

UserTask Properties

PropertyTypBeschreibung
IdstringEindeutige ID der User Task Instance
UserTaskIdstringID der User Task aus dem BPMN
NamestringName der User Task
StateUserTaskStateAktueller Status
ProcessInstanceIdstringZugehörige Prozess-Instanz
ProcessModelIdstringProzessmodell-ID
ConfigurationUserTaskConfigurationForm-Konfiguration
TokensDictionary<TokenType, Token>Ein/Ausgabe-Tokens

Form Fields auslesen

using ProcessCube.Engine; var engineAddress = "http://localhost:8000"; var client = ClientFactory.CreateUserTaskClient(engineAddress); var userTasks = await client.QueryAsync(query => { query.FilterByProcessInstanceId("abc-123"); query.FilterByState(UserTaskState.Suspended); }); var userTask = userTasks.First(); Console.WriteLine($"User Task: {userTask.Name}"); Console.WriteLine("Form Fields:"); foreach (var field in userTask.Configuration.FormFields) { Console.WriteLine($" {field.Id}: {field.Type}"); Console.WriteLine($" Label: {field.Label}"); Console.WriteLine($" Required: {field.Required}"); }

User Task abschließen

FinishUserTaskAsync()

using ProcessCube.Engine; using ProcessCube.Engine.UserTasks; var engineAddress = "http://localhost:8000"; var definitionsClient = ClientFactory.CreateProcessDefinitionsClient(engineAddress); var userTaskClient = ClientFactory.CreateUserTaskClient(engineAddress); // 1. Prozess starten (der User Task enthält) var response = await definitionsClient.StartProcessInstanceAsync( processModelId: "ApprovalProcess", startEventId: "StartEvent_1", initialToken: new { requestId = "REQ-456", amount = 5000.00m } ); var processInstanceId = response.ProcessInstanceId; Console.WriteLine($"Prozess gestartet: {processInstanceId}"); // 2. Warten bis User Task erscheint await Task.Delay(2000); // 3. User Task abrufen var userTasks = await userTaskClient.QueryAsync(query => { query.FilterByProcessInstanceId(processInstanceId); query.FilterByState(UserTaskState.Suspended); }); var userTask = userTasks.Single(); Console.WriteLine($"User Task gefunden: {userTask.Name}"); // 4. Input-Daten auslesen var inputToken = userTask.Tokens[TokenType.OnEnter]; var input = inputToken.GetPayload<ApprovalInput>(); Console.WriteLine($"Request-ID: {input.RequestId}"); Console.WriteLine($"Amount: {input.Amount:C}"); // 5. Form Field-Werte setzen var formField = userTask.Configuration.FormFields.First(); var formFieldValues = new Dictionary<string, object> { { formField.Id, "Approved" } }; var result = new UserTaskResult(formFieldValues); // 6. User Task abschließen await userTaskClient.FinishUserTaskAsync(userTask, result); Console.WriteLine("User Task abgeschlossen"); internal sealed record ApprovalInput(string RequestId, decimal Amount);

User Tasks können nur im Status Suspended abgeschlossen werden. Der Status wechselt zu Finished nach erfolgreichem Abschluss.

Typisierte Form Fields

using ProcessCube.Engine; using ProcessCube.Engine.UserTasks; var engineAddress = "http://localhost:8000"; var client = ClientFactory.CreateUserTaskClient(engineAddress); var userTasks = await client.QueryAsync(query => { query.FilterByUserTaskId("UserTask_ReviewOrder"); query.FilterByState(UserTaskState.Suspended); }); var userTask = userTasks.First(); // Input-Payload als typisiertes Objekt var input = userTask.Tokens[TokenType.OnEnter].GetPayload<OrderReviewInput>(); Console.WriteLine($"Order-ID: {input.OrderId}"); Console.WriteLine($"Customer: {input.CustomerName}"); Console.WriteLine($"Items: {input.ItemCount}"); // Form Field-Werte typisiert setzen var formData = new OrderReviewFormData( Approved: true, Comments: "Order looks good", ReviewedBy: "reviewer@company.com" ); var formFieldValues = new Dictionary<string, object> { { "approved", formData.Approved }, { "comments", formData.Comments }, { "reviewedBy", formData.ReviewedBy } }; var result = new UserTaskResult(formFieldValues); await client.FinishUserTaskAsync(userTask, result); Console.WriteLine("Order review completed"); internal sealed record OrderReviewInput( string OrderId, string CustomerName, int ItemCount ); internal sealed record OrderReviewFormData( bool Approved, string Comments, string ReviewedBy );

User Task reservieren

ReserveAsync()

Reserviert eine User Task für einen bestimmten Benutzer:

using ProcessCube.Engine; var engineAddress = "http://localhost:8000"; var client = ClientFactory.CreateUserTaskClient(engineAddress); var userTaskId = "abc-123"; var userId = "user@company.com"; // User Task reservieren await client.ReserveAsync(userTaskId, userId); Console.WriteLine($"User Task für {userId} reserviert");

CancelReservationAsync()

Hebt eine Reservierung auf:

await client.CancelReservationAsync(userTaskId); Console.WriteLine("Reservierung aufgehoben");

Batch-Verarbeitung

Mehrere User Tasks gleichzeitig verarbeiten:

using ProcessCube.Engine; var engineAddress = "http://localhost:8000"; var client = ClientFactory.CreateUserTaskClient(engineAddress); // Alle offenen Approval-Tasks var userTasks = await client.QueryAsync(query => { query.FilterByUserTaskId("UserTask_Approve"); query.FilterByState(UserTaskState.Suspended); }); Console.WriteLine($"Verarbeite {userTasks.Count} Approval-Tasks..."); foreach (var task in userTasks) { try { // Input auslesen var input = task.Tokens[TokenType.OnEnter].GetPayload<ApprovalInput>(); // Auto-Approval-Logik (z.B. für kleine Beträge) if (input.Amount < 1000) { var formField = task.Configuration.FormFields.First(); var values = new Dictionary<string, object> { { formField.Id, "Auto-Approved" } }; await client.FinishUserTaskAsync(task, new UserTaskResult(values)); Console.WriteLine($" ✓ Auto-approved: {input.RequestId}"); } else { Console.WriteLine($" ⏸ Manual review required: {input.RequestId}"); } } catch (Exception ex) { Console.WriteLine($" ✗ Error: {ex.Message}"); } } internal sealed record ApprovalInput(string RequestId, decimal Amount);

Mit Authentifizierung

using ProcessCube.Engine; using ProcessCube.Engine.Client.Identity; var identityProvider = new ClientCredentialsIdentityProvider( authorityAddress: "http://localhost:11235", clientId: "usertask-service", clientSecret: "secret-key", scope: "engine_usertask" ); var identity = await identityProvider.GetIdentityAsync(); var client = ClientFactory.CreateUserTaskClient( engineAddress: "http://localhost:8000", identity: identity ); var userTasks = await client.QueryAsync(query => { query.FilterByState(UserTaskState.Suspended); }); Console.WriteLine($"User Tasks: {userTasks.Count}");

ASP.NET Core API

using Microsoft.AspNetCore.Mvc; using ProcessCube.Engine; using ProcessCube.Engine.UserTasks; [ApiController] [Route("api/usertasks")] public class UserTaskController : ControllerBase { private readonly IUserTaskClient _client; public UserTaskController(IUserTaskClient client) { _client = client; } [HttpGet] public async Task<IActionResult> GetUserTasks( [FromQuery] string? processModelId = null) { var userTasks = await _client.QueryAsync(query => { if (!string.IsNullOrEmpty(processModelId)) { query.FilterByProcessModelId(processModelId); } query.FilterByState(UserTaskState.Suspended); }); var result = userTasks.Select(t => new { t.Id, t.Name, t.ProcessModelId, t.ProcessInstanceId, formFields = t.Configuration.FormFields.Select(f => new { f.Id, f.Label, f.Type, f.Required }) }); return Ok(result); } [HttpPost("{userTaskId}/complete")] public async Task<IActionResult> CompleteUserTask( string userTaskId, [FromBody] Dictionary<string, object> formValues) { try { // User Task abrufen var userTasks = await _client.QueryAsync(query => { query.FilterByUserTaskId(userTaskId); query.FilterByState(UserTaskState.Suspended); }); var userTask = userTasks.FirstOrDefault(); if (userTask == null) { return NotFound(new { error = "User Task nicht gefunden" }); } // Abschließen var result = new UserTaskResult(formValues); await _client.FinishUserTaskAsync(userTask, result); return Ok(new { message = "User Task abgeschlossen" }); } catch (Exception ex) { return StatusCode(500, new { error = ex.Message }); } } }

Fehlerbehandlung

using ProcessCube.Engine; using ProcessCube.Engine.Client.Exceptions; var engineAddress = "http://localhost:8000"; var client = ClientFactory.CreateUserTaskClient(engineAddress); try { var userTasks = await client.QueryAsync(query => { query.FilterByState(UserTaskState.Suspended); }); Console.WriteLine($"Gefunden: {userTasks.Count} User Tasks"); } catch (EngineClientException ex) when (ex.StatusCode == 401) { Console.WriteLine("Nicht authentifiziert"); } catch (EngineClientException ex) when (ex.StatusCode == 403) { Console.WriteLine("Fehlende Berechtigung"); } catch (HttpRequestException ex) { Console.WriteLine($"Verbindungsfehler: {ex.Message}"); }

Best Practice: Verwenden Sie typisierte Records für Input-Payloads und Form-Daten, um Type Safety und IntelliSense zu erhalten.

Nächste Schritte