如何在Elsa Workflow中按条件停止Timer并实现3次间隔提醒后终止
Elsa Workflow 提醒邮件与自动终止实现问题
我是Elsa Workflow新手,表述不当之处请见谅。我已自定义了文档审批工作流,目前运行正常。现在需要实现以下场景:
- 仅发送3次不同间隔的提醒邮件:
- 首次提醒:审批邮件发送1小时后
- 第二次提醒:审批邮件发送5小时后
- 第三次提醒:审批邮件发送24小时后
- 第三次提醒后再等待24小时,若仍无响应则取消工作流或标记为故障
- 提醒次数不得超过3次,最终工作流需终止为故障或取消状态
我已创建自定义方法GetTimerValue()和CheckReminderLimitReached(),通过本地文件存储计数器(不使用数据库)跟踪提醒发送次数:
GetTimerValue():根据当前计数器值返回对应提醒的Duration并将计数器加1CheckReminderLimitReached():返回计数器是否达到上限
我尝试了如下代码但未能实现需求,已卡5天,恳请帮助。
项目信息
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> </PropertyGroup> <ItemGroup> <PackageReference Include="Elsa" Version="2.13.0" /> <PackageReference Include="Elsa.Activities.Email" Version="2.13.0" /> <PackageReference Include="Elsa.Activities.Http" Version="2.13.0" /> <PackageReference Include="Elsa.Activities.Temporal.Quartz" Version="2.13.0" /> <PackageReference Include="Elsa.Designer.Components.Web" Version="2.13.0" /> <PackageReference Include="Elsa.Persistence.EntityFramework.Sqlite" Version="2.13.0" /> <PackageReference Include="Elsa.Scripting.JavaScript" Version="2.13.0" /> <PackageReference Include="Elsa.Scripting.Liquid" Version="2.13.0" /> <PackageReference Include="Elsa.Server.Api" Version="2.13.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Log4Net.AspNetCore" Version="7.0.0" /> </ItemGroup> <ItemGroup> <Folder Include="Utility\" /> </ItemGroup> <ItemGroup> <Content Update="log4net.config"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> </ItemGroup> </Project>
尝试的工作流代码
fork .When("Remind") //.Timer(Duration.FromSeconds(0)).WithName("Reminder") //.IfTrue(CheckReminderLimitReached,context=>context.ThenNamed("Reminder").WorkflowBuilder.Finish()) //.IfFalse(CheckReminderLimitReached) //.If(context => CheckReminderLimitReached(context)) // .When("true") // .CancelTimer(context => context.ActivityId).Break() .Timer(context => GetTimerValue(context)).WithName("Reminder") .If(context=> CheckReminderLimitReached(context)).When("true").Timer().CancelTimer(context => context.ActivityId).Finish() .SendEmail( // some code )
自定义方法代码
private bool CheckReminderLimitReached(ActivityExecutionContext context) { string ticketId = context.GetVariable<ApprovalRequestModel>("Approval")!.ticket_id!; string path = $"C:\\Apps\\DotNet\\UatTickets\\{ticketId}\\rem_counter.txt"; var content = string.Empty; var reminders = 0; if (File.Exists(path)) { content = File.ReadAllText(path); } if (!string.IsNullOrEmpty(content)) { reminders = Int32.Parse(content); } bool result = reminders == 3; return result; } private Duration GetTimerValue(ActivityExecutionContext context) { string ticketId = context.GetVariable<ApprovalRequestModel>("Approval")!.ticket_id!; string path = $"C:\\Apps\\DotNet\\UatTickets\\{ticketId}\\rem_counter.txt"; var timerDict = new Dictionary<int, int>() { {0,60 }, {1,120 }, {2,180 }, {3,180 } }; var reminderCount = 0; var content = File.ReadAllText(path); if (!string.IsNullOrEmpty(content)) { reminderCount = Int32.Parse(content); } // increase the counter File.WriteAllText(path, $"{reminderCount+1}"); //return Duration.FromSeconds(10); var result = Duration.FromSeconds(timerDict[reminderCount]); return result; }
解决方案
问题分析
- 原工作流缺少循环触发提醒的逻辑,仅执行一次Timer和SendEmail,无法实现多次提醒
GetTimerValue中的时间配置错误,当前用秒数(60/120/180),不符合需求的小时间隔- 缺少第三次提醒后等待24小时再终止的逻辑
- 文件操作未加锁,可能出现并发读写导致计数器错乱
修正后的工作流代码
// 主分支:保留原有审批响应处理逻辑 fork .When("ApprovalResponse") // 此处添加原有审批完成的处理逻辑(如接收回调、标记工作流完成) .Finish() .When("RemindLoop") .If(context => !CheckReminderLimitReached(context)) .When("true") .Timer(context => GetTimerValue(context)).WithName("ReminderTimer") .SendEmail(/* 你的邮件发送逻辑 */) .Goto("RemindLoop") // 循环回到判断节点,继续检查是否需要下一次提醒 .When("false") .Timer(Duration.FromHours(24)) // 第三次提醒后等待24小时 .MarkAsFaulted("审批超时未响应") // 或使用.CancelWorkflow()取消工作流 .Finish()
修正后的自定义方法
private bool CheckReminderLimitReached(ActivityExecutionContext context) { string ticketId = context.GetVariable<ApprovalRequestModel>("Approval")!.ticket_id!; string path = $"C:\\Apps\\DotNet\\UatTickets\\{ticketId}\\rem_counter.txt"; int reminders = 0; // 加锁保证文件读写原子性,避免并发问题 lock (this) { if (File.Exists(path)) { var content = File.ReadAllText(path); int.TryParse(content, out reminders); } } return reminders >= 3; // 改为>=3更严谨,避免计数器异常溢出 } private Duration GetTimerValue(ActivityExecutionContext context) { string ticketId = context.GetVariable<ApprovalRequestModel>("Approval")!.ticket_id!; string path = $"C:\\Apps\\DotNet\\UatTickets\\{ticketId}\\rem_counter.txt"; int reminderCount = 0; lock (this) { if (File.Exists(path)) { var content = File.ReadAllText(path); int.TryParse(content, out reminderCount); } // 更新计数器 File.WriteAllText(path, $"{reminderCount + 1}"); } // 修正时间配置为需求的小时数 var timerDict = new Dictionary<int, Duration>() { {0, Duration.FromHours(1) }, {1, Duration.FromHours(5) }, {2, Duration.FromHours(24) } }; // 容错处理:避免计数器超出范围导致KeyNotFoundException return timerDict.TryGetValue(reminderCount, out var duration) ? duration : Duration.Zero; }
关键说明
- 循环逻辑:通过
Goto("RemindLoop")实现提醒循环,每次发送邮件后回到判断节点,检查是否达到提醒上限 - 时间修正:将
GetTimerValue中的时间单位从秒改为小时,完全匹配需求间隔 - 并发安全:添加
lock确保文件读写操作原子性,防止计数器错乱 - 超时终止:提醒次数耗尽后,等待24小时再标记工作流为故障或取消
- 容错处理:使用
int.TryParse避免文件内容异常导致程序崩溃
内容的提问来源于stack exchange,提问作者Chetan Kumar
相关产品推荐
相关产品推荐

