.Net工作流elsa-workflows官方文档学习:简单的文件审批工作流
网页:https://elsa-workflows.github.io/elsa-core/docs/guides-document-approval
在本节中,我们将执行以下操作:
- 以编程方式定义长时间运行的工作流,在HTTP请求到达指定URL时执行,接受带有JSON负载的POST请求,该JSON负载表示要检查的文档。
- 相关活动组件:ReceiveHttpRequest,WriteHttpResponse,Fork,Join,SetVariable,Signaled,SendEmail和IfElse。
此工作流的目的是允许作者提交文档(建模为JSON对象),并允许审阅者批准或拒绝此文档。 此外,如果审阅者花费的时间太长而无法采取行动,则会定期提醒他。
我们将发布到工作流中的JSON有效负载如下所示:

{ "Id": "1", "Author": { "Name": "John", "Email": "john@gmail.com" }, "Body": "This is sample document." }
使用访问此模型的JavaScript表达式查看活动时,请牢记此结构。
创建ASP.NET Core项目
创建一个名为Elsa.Guides.ContentApproval.WebApp的新的空ASP.NET Core项目,并添加以下程序包:
- Elsa.Core
- Elsa.Activities.Email
- Elsa.Activities.Http
- Elsa.Activities.Timers
创建工作流类

using System; using System.Dynamic; using System.Net; using System.Net.Http; using Elsa.Activities.ControlFlow; using Elsa.Activities.Email.Activities; using Elsa.Activities.Http.Activities; using Elsa.Activities.Primitives; using Elsa.Activities.Timers.Activities; using Elsa.Activities.Workflows; using Elsa.Expressions; using Elsa.Services; using Elsa.Services.Models; namespace Elsa.Guides.DocumentApproval.WebApp { public class DocumentApprovalWorkflow : IWorkflow { public void Build(IWorkflowBuilder builder) { builder .StartWith<ReceiveHttpRequest>( x => { x.Method = HttpMethod.Post.Method; x.Path = new Uri("/documents", UriKind.Relative); x.ReadContent = true; } ) .Then<SetVariable>( x => { x.VariableName = "Document"; x.ValueExpression = new JavaScriptExpression<ExpandoObject>("lastResult().Body"); } ) .Then<SendEmail>( x => { x.From = new LiteralExpression("approval@acme.com"); x.To = new JavaScriptExpression<string>("Document.Author.Email"); x.Subject = new JavaScriptExpression<string>("`Document received from ${Document.Author.Name}`"); x.Body = new JavaScriptExpression<string>( "`Document from ${Document.Author.Name} received for review. " + "<a href=\"${signalUrl('Approve')}\">Approve</a> or <a href=\"${signalUrl('Reject')}\">Reject</a>`" ); } ) .Then<WriteHttpResponse>( x => { x.Content = new LiteralExpression( "<h1>Request for Approval Sent</h1><p>Your document has been received and will be reviewed shortly.</p>" ); x.ContentType = "text/html"; x.StatusCode = HttpStatusCode.OK; x.ResponseHeaders = new LiteralExpression("X-Powered-By=Elsa Workflows"); } ) .Then<SetVariable>( x => { x.VariableName = "Approved"; x.ValueExpression = new JavaScriptExpression<bool>("false"); } ) .Then<Fork>( x => { x.Branches = new[] { "Approve", "Reject", "Remind" }; }, fork => { fork .When("Approve") .Then<Signaled>(x => x.Signal = new LiteralExpression("Approve")) .Then("Join"); fork .When("Reject") .Then<Signaled>(x => x.Signal = new LiteralExpression("Reject")) .Then("Join"); fork .When("Remind") .Then<TimerEvent>( x => x.TimeoutExpression = new LiteralExpression<TimeSpan>("00:00:10"), name: "RemindTimer" ) .Then<IfElse>( x => x.ConditionExpression = new JavaScriptExpression<bool>("!!Approved"), ifElse => { ifElse .When(OutcomeNames.False) .Then<SendEmail>( x => { x.From = new LiteralExpression("reminder@acme.com"); x.To = new LiteralExpression("approval@acme.com"); x.Subject = new JavaScriptExpression<string>( "`${Document.Author.Name} is awaiting for your review!`" ); x.Body = new JavaScriptExpression<string>( "`Don't forget to review document ${Document.Id}.<br/>" + "<a href=\"${signalUrl('Approve')}\">Approve</a> or <a href=\"${signalUrl('Reject')}\">Reject</a>`" ); } ) .Then("RemindTimer"); } ); } ) .Then<Join>(x => x.Mode = Join.JoinMode.WaitAny, name: "Join") .Then<SetVariable>( x => { x.VariableName = "Approved"; x.ValueExpression = new JavaScriptExpression<object>("input('Signal') === 'Approve'"); } ) .Then<IfElse>( x => x.ConditionExpression = new JavaScriptExpression<bool>("!!Approved"), ifElse => { ifElse .When(OutcomeNames.True) .Then<SendEmail>( x => { x.From = new LiteralExpression("approval@acme.com"); x.To = new JavaScriptExpression<string>("Document.Author.Email"); x.Subject = new JavaScriptExpression<string>("`Document ${Document.Id} approved!`"); x.Body = new JavaScriptExpression<string>( "`Great job ${Document.Author.Name}, that document is perfect! Keep it up.`" ); } ); ifElse .When(OutcomeNames.False) .Then<SendEmail>( x => { x.From = new LiteralExpression("approval@acme.com"); x.To = new JavaScriptExpression<string>("Document.Author.Email"); x.Subject = new JavaScriptExpression<string>("`Document ${Document.Id} rejected`"); x.Body = new JavaScriptExpression<string>( "`Sorry ${Document.Author.Name}, that document isn't good enough. Please try again.`" ); } ); } ); } } }
ReceiveHttpRequest
.StartWith<ReceiveHttpRequest>( x => { x.Method = HttpMethod.Post.Method; x.Path = new Uri("/documents", UriKind.Relative); x.ReadContent = true; } )
标签:
elsa-workflows
, .Net
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人