Emberi interakciós minta

Az emberi interakciós minta olyan munkafolyamatokat ír le, amelyek szüneteltetik és várják a személyek bemenetét a folytatás előtt. A minta hasznos a jóváhagyási munkafolyamatokhoz, a többtényezős hitelesítéshez és minden olyan forgatókönyvhöz, amikor egy személy egy adott határidőn belül válaszol.

Magas szinten a minta a következőképpen működik:

  1. A vezénylő meghív egy tevékenységet egy személy értesítésére (SMS-kód küldése, jóváhagyó e-mail küldése stb.).
  2. A vezénylő elindít egy tartós időzítőt, és egyidejűleg vár egy külső eseményre a személytől.
  3. Ha a személy az időzítő kilövése előtt válaszol, a vezénylő feldolgozza a választ.
  4. Ha az időzítő először aktiválódik, a vezénylő kezeli az időtúllépést (például a kérés elutasításával).

A cikk tartalma:

Megjegyzés:

A Durable Functions és a Durable Task SDK kimutatásai ugyanazt a mintát szemléltetik különböző forgatókönyvekkel: a Durable Functions SMS alapú telefonos hitelesítést példát alkalmaz, míg a Durable Task SDK-k jóváhagyási munkafolyamatot példát alkalmaznak.

Ez a minta bemutatja, hogyan hozhat létre Durable Functions vezénylést, amely magában foglalja az emberi interakciót. A példa egy SMS-alapú telefonos ellenőrző rendszert implementál. Ez gyakori a telefonszám-ellenőrzés és a többtényezős hitelesítés (MFA) folyamatokban.

Megjegyzés:

A C#, JavaScript és Python teljes kódminták érhetők el. A PowerShell- és Java-minták jelenleg nem érhetők el.

Megjegyzés:

Az Azure Functions Node.js programozási modelljének 4. verziója általánosan elérhető. A v4-es modell úgy lett kialakítva, hogy rugalmasabb és intuitívabb élményt nyújtson a JavaScript- és TypeScript-fejlesztők számára. A v3 és a v4 közötti különbségekről további információt a migrálási útmutatóban talál.

A következő kódrészletekben a JavaScript (PM4) a v4-es programozási modellt, az új élményt jelöli.

Előfeltételek

Ez a cikk bemutatja, hogyan implementálható az emberi interakciós minta a Durable Task SDK-k használatával. A példa egy jóváhagyási munkafolyamatot valósít meg, amelyben egy orchestráció megvárja, hogy egy személy jóváhagyja vagy elutasítsa a kérelmet, mielőtt folytatná.

Emberi interakciós forgatókönyv áttekintése

A telefonos ellenőrzés segít meggyőződni arról, hogy az alkalmazást használó személyek nem levélszemétküldők, és hogy ők irányítják a megadott telefonszámot. A fiókok védelmének gyakori módja a többtényezős hitelesítés. A saját telefonos ellenőrzés létrehozásához állapotalapú interakcióra van szükség egy személlyel. A felhasználó általában kap egy kódot (például egy négyjegyű számot), és ésszerű időn belül válaszolnia kell.

A standard Azure Functions állapot nélküliek (mint sok más felhővégpont), ezért az ilyen típusú interakciókhoz állapotot kell tárolnia egy adatbázisban vagy egy másik állandó tárolóban. Az interakciót több függvényre is feloszthatja, és összehangolhatja őket. Egy függvény például létrehoz egy kódot, tárolja és elküldi a felhasználó telefonjára. Egy másik függvény megkapja a felhasználó válaszát, és leképozza az eredeti kéréshez a kód érvényesítéséhez. Időtúllépés beállítása a biztonság fokozása érdekében. Ez a munkafolyamat gyorsan összetetté válik.

Durable Functions csökkenti a forgatókönyv összetettségét. Ebben a mintában egy vezénylő függvény külső adattár nélkül kezeli az állapotalapú interakciót. Mivel a vezénylő függvények tartósak, ezek az interaktív folyamatok rendkívül megbízhatóak.

A jóváhagyási munkafolyamatok gyakoriak az üzleti alkalmazásokban, ahol a kéréseket egy embernek felül kell vizsgálnia a folytatás előtt. A munkafolyamat követelményei a következők:

  • Várjon határozatlan ideig az emberi válaszra, vagy amíg időtúllépést nem ér el
  • A jóváhagyás és az elutasítás kimenetelének kezelése
  • Időtúllépések támogatása ha nem érkezett válasz
  • Állapot nyomon követése , hogy a kérelmező ellenőrizni tudja az állapotot

A Durable Task SDK-k leegyszerűsítik ezt a forgatókönyvet a következőkkel:

  • Külső események: A vezénylés szüneteltetheti és megvárhatja a külső rendszer vagy felhasználó által kiváltott eseményeket
  • Tartós időzítők: Állítson be egy időtúllépést, amely aktiválódik, ha nem érkezik válasz
  • Egyéni állapot: Az aktuális munkafolyamat állapotának nyomon követése és az ügyfelek számára való elérhetővé helyezése

Twilio-integráció konfigurálása

Ez a minta magában foglalja a Twilio szolgáltatás használatát SMS-üzenetek mobiltelefonra való küldéséhez. Azure Functions már támogatja a Twilio-t a Twilio kötésen keresztül, és a minta ezt a funkciót használja.

Az első dolog, amire szüksége van, egy Twilio-fiók. Létrehozhat egyet ingyenesen a .. címen https://www.twilio.com/try-twilio. Miután rendelkezik fiókkal, adja hozzá az alábbi három alkalmazásbeállítást a függvényalkalmazáshoz.

Alkalmazásbeállítás neve Érték leírása
TwilioAccountSid A Twilio-fiók SID-azonosítója
TwilioAuthToken A Twilio-fiók hitelesítési jogkivonata
TwilioPhoneNumber A Twilio-fiókhoz társított telefonszám. Ez SMS-üzenetek küldésére szolgál.

Az orchesztrátor definiálása

E4_SmsPhoneVerification vezérlő függvény

[FunctionName("E4_SmsPhoneVerification")]
public static async Task<bool> Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    string phoneNumber = context.GetInput<string>();
    if (string.IsNullOrEmpty(phoneNumber))
    {
        throw new ArgumentNullException(
            nameof(phoneNumber),
            "A phone number input is required.");
    }

    int challengeCode = await context.CallActivityAsync<int>(
        "E4_SendSmsChallenge",
        phoneNumber);

    using (var timeoutCts = new CancellationTokenSource())
    {
        // The user has 90 seconds to respond with the code they received in the SMS message.
        DateTime expiration = context.CurrentUtcDateTime.AddSeconds(90);
        Task timeoutTask = context.CreateTimer(expiration, timeoutCts.Token);

        bool authorized = false;
        for (int retryCount = 0; retryCount <= 3; retryCount++)
        {
            Task<int> challengeResponseTask =
                context.WaitForExternalEvent<int>("SmsChallengeResponse");

            Task winner = await Task.WhenAny(challengeResponseTask, timeoutTask);
            if (winner == challengeResponseTask)
            {
                // We got back a response! Compare it to the challenge code.
                if (challengeResponseTask.Result == challengeCode)
                {
                    authorized = true;
                    break;
                }
            }
            else
            {
                // Timeout expired
                break;
            }
        }

        if (!timeoutTask.IsCompleted)
        {
            // All pending timers must be complete or canceled before the function exits.
            timeoutCts.Cancel();
        }

        return authorized;
    }
}

Megjegyzés:

Elsőre talán nem nyilvánvaló, de ez a vezénylő nem sérti a determinisztikus vezénylési kényszert. Ez determinisztikus, mert a CurrentUtcDateTime tulajdonság kiszámítja az időzítő lejárati idejét, és ugyanazt az értéket adja vissza minden visszajátszáskor a vezénylési kódban. Ez a viselkedés biztosítja, hogy winner minden ismétlődő hívás Task.WhenAnyesetében ugyanaz legyen.

A kezdés után ez a vezénylő függvény a következőket végzi el:

  1. Lekéri a telefonszámot az SMS-értesítés elküldéséhez.
  2. Felhívja E4_SendSmsChallenge , hogy küldjön SMS-üzenetet a felhasználónak, és visszaadja a várt négyjegyű kihíváskódot.
  3. Tartós időzítőt hoz létre, amely az aktuális idő után 90 másodpercet aktivál.
  4. Az időzítővel párhuzamosan egy SmsChallengeResponse eseményt vár a felhasználótól.

A felhasználó egy négyjegyű kóddal rendelkező SMS-üzenetet kap. 90 másodpercük van arra, hogy ugyanazt a kódot elküldjék a vezénylő példánynak az ellenőrzés befejezéséhez. Ha nem a megfelelő kódot küldik el, még három próbálkozást kapnak ugyanazon a 90 másodperces ablakban.

Figyelmeztetés

Megszakíthatja az időzítőket , amelyekre már nincs szüksége. A fenti példában a vezénylés megszakítja az időzítőt, amikor elfogadja a kihívásra adott választ.

Az orchesztrátor elküld egy jóváhagyási kérelmet, majd megvárja az emberi választ vagy az időtúllépést.

using Microsoft.DurableTask;
using System;
using System.Threading;
using System.Threading.Tasks;

[DurableTask(nameof(ApprovalOrchestration))]
public class ApprovalOrchestration : TaskOrchestrator<ApprovalRequestData, ApprovalResult>
{
    public override async Task<ApprovalResult> RunAsync(
        TaskOrchestrationContext context, ApprovalRequestData input)
    {
        string requestId = input.RequestId;
        double timeoutHours = input.TimeoutHours;

        // Step 1: Submit the approval request (notify approver)
        SubmissionResult submissionResult = await context.CallActivityAsync<SubmissionResult>(
            nameof(SubmitApprovalRequestActivity), input);

        // Make the status available via custom status
        context.SetCustomStatus(submissionResult);

        // Step 2: Create a durable timer for the timeout
        DateTime timeoutDeadline = context.CurrentUtcDateTime.AddHours(timeoutHours);

        using var timeoutCts = new CancellationTokenSource();
        Task timeoutTask = context.CreateTimer(timeoutDeadline, timeoutCts.Token);

        // Step 3: Wait for an external event (approval/rejection)
        Task<ApprovalResponseData> approvalTask = context.WaitForExternalEvent<ApprovalResponseData>(
            "approval_response");

        // Step 4: Wait for either the timeout or the approval response
        Task completedTask = await Task.WhenAny(approvalTask, timeoutTask);

        // Step 5: Process based on which task completed
        ApprovalResult result;

        if (completedTask == approvalTask)
        {
            // Human responded in time - cancel the timeout timer
            timeoutCts.Cancel();

            ApprovalResponseData approvalData = approvalTask.Result;

            // Process the approval
            result = await context.CallActivityAsync<ApprovalResult>(
                nameof(ProcessApprovalActivity),
                new ProcessApprovalInput
                {
                    RequestId = requestId,
                    IsApproved = approvalData.IsApproved,
                    Approver = approvalData.Approver
                });
        }
        else
        {
            // Timeout occurred
            result = new ApprovalResult
            {
                RequestId = requestId,
                Status = "Timeout",
                ProcessedAt = context.CurrentUtcDateTime.ToString("o")
            };
        }

        return result;
    }
}

Ez a vezénylő a következő műveleteket hajtja végre:

  1. A jóváhagyási kérelmet egy olyan tevékenység meghívásával küldi el, amely értesíti a jóváhagyót.
  2. Beállítja az egyéni állapotot, hogy az ügyfelek nyomon tudják követni az előrehaladást.
  3. Tartós időzítőt hoz létre az időtúllépési határidőhöz.
  4. A jóváhagyó által kiváltott külső eseményre (approval_response) vár.
  5. A WhenAny, when_any vagy anyOf elemeket használja arra, hogy megvárja, melyik fejeződik be először: a jóváhagyás vagy az időtúllépés.
  6. Az, hogy melyik feladat fejeződik be, alapján dolgozza fel az eredményt.

Figyelmeztetés

Megszakíthatja az időzítőket , amelyekre már nincs szüksége. Az C# példában az orchestráció megszakítja az időtúllépési időzítőt, amikor jóváhagyást kap.

A tevékenységek meghatározása

E4_SendSmsChallenge tevékenységfüggvény

A E4_SendSmsChallenge függvény a Twilio-kötéssel küld egy négyjegyű kódot tartalmazó SMS-üzenetet a felhasználónak.

[FunctionName("E4_SendSmsChallenge")]
public static int SendSmsChallenge(
    [ActivityTrigger] string phoneNumber,
    ILogger log,
    [TwilioSms(AccountSidSetting = "TwilioAccountSid", AuthTokenSetting = "TwilioAuthToken", From = "%TwilioPhoneNumber%")]
        out CreateMessageOptions message)
{
    // Get a random number generator with a random seed (not time-based)
    var rand = new Random(Guid.NewGuid().GetHashCode());
    int challengeCode = rand.Next(10000);

    log.LogInformation($"Sending verification code {challengeCode} to {phoneNumber}.");

    message = new CreateMessageOptions(new PhoneNumber(phoneNumber));
    message.Body = $"Your verification code is {challengeCode:0000}";

    return challengeCode;
}

Megjegyzés:

A minta futtatásához telepítse a Microsoft.Azure.WebJobs.Extensions.Twilio NuGet-csomagot. Ne telepítse a fő Twilio NuGet-csomagot , mert verzióütközéseket és buildelési hibákat okozhat.

A tevékenységek elküldik a jóváhagyási kérelmet, és feldolgozzák a választ.

Jóváhagyási kérelem tevékenységének elküldése

using Microsoft.DurableTask;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;

[DurableTask(nameof(SubmitApprovalRequestActivity))]
public class SubmitApprovalRequestActivity : TaskActivity<ApprovalRequestData, SubmissionResult>
{
    private readonly ILogger<SubmitApprovalRequestActivity> _logger;

    public SubmitApprovalRequestActivity(ILogger<SubmitApprovalRequestActivity> logger)
    {
        _logger = logger;
    }

    public override Task<SubmissionResult> RunAsync(
        TaskActivityContext context, ApprovalRequestData input)
    {
        _logger.LogInformation(
            "Submitting approval request {RequestId} from {Requester} for {Item}",
            input.RequestId, input.Requester, input.Item);

        // In a real system, this would send an email, notification, or update a database
        var result = new SubmissionResult
        {
            RequestId = input.RequestId,
            Status = "Pending",
            SubmittedAt = DateTime.UtcNow.ToString("o"),
            ApprovalUrl = $"http://localhost:8000/api/approvals/{input.RequestId}"
        };

        return Task.FromResult(result);
    }
}

Folyamat-jóváhagyási tevékenység

using Microsoft.DurableTask;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;

[DurableTask(nameof(ProcessApprovalActivity))]
public class ProcessApprovalActivity : TaskActivity<ProcessApprovalInput, ApprovalResult>
{
    private readonly ILogger<ProcessApprovalActivity> _logger;

    public ProcessApprovalActivity(ILogger<ProcessApprovalActivity> logger)
    {
        _logger = logger;
    }

    public override Task<ApprovalResult> RunAsync(
        TaskActivityContext context, ProcessApprovalInput input)
    {
        string status = input.IsApproved ? "Approved" : "Rejected";
        _logger.LogInformation(
            "Processing {Status} request {RequestId} by {Approver}",
            status, input.RequestId, input.Approver);

        // In a real system, this would update a database, trigger workflows, etc.
        var result = new ApprovalResult
        {
            RequestId = input.RequestId,
            Status = status,
            ProcessedAt = DateTime.UtcNow.ToString("o"),
            Approver = input.Approver
        };

        return Task.FromResult(result);
    }
}

// Data classes
public class ApprovalRequestData
{
    public string RequestId { get; set; } = string.Empty;
    public string Requester { get; set; } = string.Empty;
    public string Item { get; set; } = string.Empty;
    public double TimeoutHours { get; set; } = 24.0;
}

public class ApprovalResponseData
{
    public bool IsApproved { get; set; }
    public string Approver { get; set; } = string.Empty;
}

public class SubmissionResult
{
    public string RequestId { get; set; } = string.Empty;
    public string Status { get; set; } = string.Empty;
    public string SubmittedAt { get; set; } = string.Empty;
    public string ApprovalUrl { get; set; } = string.Empty;
}

public class ProcessApprovalInput
{
    public string RequestId { get; set; } = string.Empty;
    public bool IsApproved { get; set; }
    public string Approver { get; set; } = string.Empty;
}

public class ApprovalResult
{
    public string RequestId { get; set; } = string.Empty;
    public string Status { get; set; } = string.Empty;
    public string ProcessedAt { get; set; } = string.Empty;
    public string? Approver { get; set; }
}

Az emberi interakciós minta futtatása

Használja a mintában lévő HTTP-kérések által aktivált függvényeket az orchestration elindításához a következő HTTP POST-kérés elküldésével:

POST http://{host}/orchestrators/E4_SmsPhoneVerification
Content-Length: 14
Content-Type: application/json

"+1425XXXXXXX"
HTTP/1.1 202 Accepted
Content-Type: application/json; charset=utf-8

{"id":"741c65651d4c40cea29acdd5bb47baf1",
 "sendEventPostUri":"http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1/raiseEvent/{eventName}?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}",
 "statusQueryGetUri":"http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1?taskHub=...&code={systemKey}",
 "terminatePostUri":"http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1/terminate?reason={text}&taskHub=...&code={systemKey}"}

A vezénylő függvény megkapja a telefonszámot, és azonnal sms-t küld erre a számra egy véletlenszerűen generált 4 jegyű ellenőrző kóddal – például 2168. A függvény ezután 90 másodpercet vár a válaszig.

A kóddal való válaszadáshoz használja a RaiseEventAsync (.NET) vagy raiseEvent (JavaScript és TypeScript) egy másik függvényben, vagy hívja meg a sendEventPostUri HTTP POST végpontot a 202-es válaszban. Cserélje le {eventName} a következőre SmsChallengeResponse:

POST http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1/raiseEvent/SmsChallengeResponse?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}
Content-Length: 4
Content-Type: application/json

2168

Ha az eseményt az időzítő lejárta előtt küldi el, a vezénylés befejeződik, és a output mező értéke truea sikeres ellenőrzést jelzi.

GET http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}
HTTP/1.1 200 OK
Content-Length: 144
Content-Type: application/json; charset=utf-8

{"runtimeStatus":"Completed","input":"+1425XXXXXXX","output":true,"createdTime":"2026-04-23T19:10:49Z","lastUpdatedTime":"2026-04-23T19:12:23Z"}

Ha az időzítő lejár, vagy négyszer hibás kódot ad meg, kérdezze le az állapotot, hogy megtudja, output érték lett-e false, ami azt jelzi, hogy a telefonos ellenőrzés sikertelen volt.

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 145

{"runtimeStatus":"Completed","input":"+1425XXXXXXX","output":false,"createdTime":"2026-04-23T19:20:49Z","lastUpdatedTime":"2026-04-23T19:22:23Z"}

A minta futtatásához:

  1. Indítsa el a Durable Task Scheduler emulatort a helyi fejlesztéshez. A Dockert telepíteni kell.

    docker run -d -p 8080:8080 -p 8082:8082 --name dts-emulator mcr.microsoft.com/dts/dts-emulator:latest
    
  2. Indítsa el a feldolgozót a vezénylő és a tevékenységek regisztrálásához.

  3. Futtassa a klienst egy jóváhagyási munkafolyamat ütemezése és események küldése érdekében.

using System;
using System.Threading.Tasks;

var client = DurableTaskClientBuilder.UseDurableTaskScheduler(connectionString).Build();

// Schedule the approval workflow
var input = new ApprovalRequestData
{
    RequestId = "request-" + Guid.NewGuid().ToString(),
    Requester = "john.doe@example.com",
    Item = "Vacation Request - 5 days",
    TimeoutHours = 24
};

string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
    nameof(ApprovalOrchestration), input);

Console.WriteLine($"Started approval workflow: {instanceId}");

// Simulate human approving the request
Console.WriteLine("Simulating approval...");
await Task.Delay(2000);

// Raise the approval event
var approvalResponse = new ApprovalResponseData
{
    IsApproved = true,
    Approver = "manager@example.com"
};

await client.RaiseEventAsync(instanceId, "approval_response", approvalResponse);

// Wait for completion
var result = await client.WaitForInstanceCompletionAsync(instanceId, getInputsAndOutputs: true);
Console.WriteLine($"Result: {result.ReadOutputAs<ApprovalResult>().Status}");

Következő lépések

Ez a minta speciális Durable Functions képességeket mutat be, beleértve a WaitForExternalEvent és CreateTimer API-kat. Bemutatja, hogyan kombinálható a Task.WhenAny (C#), a context.df.Task.any (JavaScript és TypeScript), illetve a context.task_any (Python) a megbízható időtúllépési minta implementálása olyan munkafolyamatokhoz, amelyek megvárják, amíg a felhasználók válaszolnak.

Ez a minta bemutatja, hogyan lehet a Durable Task SDK-k használatával olyan munkafolyamatokat implementálni, amelyek várnak az emberek válaszára, konfigurálható időtúllépésekkel. Főbb fogalmak:

  • Külső események: WaitForExternalEvent bemenetre való várakozás
  • Tartós időzítők: CreateTimeridőtúllépések implementálása
  • Versenyfeladatok: A WhenAny, when_any, vagy anyOf használata annak kezelése érdekében, amelyik feladat először fejeződik be