×

.NET 终于有了自己的钉钉审批流引擎:AntFlowCore 源码深度解析

独孤求败 独孤求败 发表于2026-05-07 14:33:14 浏览28 评论0

抢沙发发表评论

在 .NET 的开源生态里,工作流引擎从来不是个新鲜话题。从 Windows Workflow Foundation 到 Elsa,从 Flowable 的 .NET 移植到各种自研方案,这个领域从来不乏探索者。

但 AntFlowCore(也叫 AntFlow.NET)这个项目,还是让我在翻开源码后停不下来。

原因很简单:它没有选择"翻译"Java 版 AntFlow,而是用 .NET 生态的工具链重新解了同一道题。 而且解题方式,相当有意思。

这个项目 2024 年 11 月起步,到现在的完成度——19 种流程操作、22 个事件钩子接口、9 个表单生命周期方法、11 个分层项目、Apache 2.0 开源——说实话,不像一个人的业余项目。

今天就来拆开看看,里面到底装了什么。


https://github.com/mrtylerzhou/AntFlow.net


图片


一、先说背景:为什么是钉钉审批流

AntFlowCore 的灵感来自钉钉的审批流设计——国内最被低估的产品之一。

用过钉钉审批的人都知道,它的强大不在于"画流程图",而在于对中国企业组织架构的理解。直属领导审批、HRBP 会签、按职级跳转、跨部门加签……这些在欧美 BPMN 标准里不存在的需求,在钉钉里是标配。

原版 AntFlow 是 Java 实现的,2024 年 5 月启动。而 .NET 版本(AntFlowCore)半年后跟进,瞄准 .NET 10 和 C# 13,定位很明确:让 .NET 生态的企业级应用也能用上这套审批流引擎

图片

二、架构全景:11 个项目的分层逻辑

图片

AntFlowCore 的解决方案被拆成了 11 个项目,这不是为了看起来"专业",而是有清晰的依赖方向:

AntFlowCore.Web          ← 入口,Minimal API 宿主
    ↓
AntFlowCore.Api          ← REST 控制器
    ↓
AntFlowCore.AspNetCore   ← DI 注册、中间件、FreeSql 配置
    ↓
    ├── AntFlowCore.Engine      ← 业务服务、工厂、处理器链
    │       ↓
    │   AntFlowCore.Bpmn        ← BPMN 引擎核心:服务、适配器、监听器
    │       ↓
    │   AntFlowCore.Base        ← 领域模型:实体、BPMN 模型、枚举、DTO
    │
    ├── AntFlowCore.Persist           ← FreeSql 仓储实现
    ├── AntFlowCore.Persist.api       ← 仓储接口(68 个)
    ├── AntFlowCore.Abstraction       ← 服务接口、AOP、格式化器
    └── AntFlowCore.Abstraction.Orm   ← 仓储抽象基类

注:解决方案中还有 AntFlowCore.Engine.Orm,无源码文件,仅用于 NuGet 打包。

依赖方向很干净:Web → Api → AspNetCore → (Engine → Bpmn → Base) + (Persist → Persist.api → Abstraction.Orm)。没有循环依赖,没有跨层调用。

AntFlowCore.Base 是地基,60 多个数据库实体、80 多个 VO、50 多个枚举类型,定义了系统的每个角落。Bpmn 是引擎核心,负责流程的创建、执行、流转。Engine 是业务层,把引擎能力包装成可用服务。Persist 用 FreeSql 做了 68 个仓储接口和实现。

这种分层不是教科书式的"为了分层而分层",而是每个层都有明确的职责边界和可替换性。比如你可以把 FreeSql 换成 EF Core,只需要重写 Persist 层,上面的引擎和业务逻辑完全不用动。


三、核心技术亮点

3.1 Natasha 动态工厂:让编译器替你写代码

这是整个项目最值得细说的部分。

在传统的策略模式实现中,工厂方法通常长这样:

publicclassAdaptorFactory : IAdaptorFactory
{
public IProcessOperationAdaptor GetProcessOperationAdaptor(string type)
    {
return type switch
        {
"Submit" => _submitAdaptor,
"Agree" => _agreeAdaptor,
"DisAgree" => _disagreeAdaptor,
"Transfer" => _transferAdaptor,
"Delegate" => _delegateAdaptor,
"BackToModify" => _backToModifyAdaptor,
// ... 19 种操作,19 个 case
            _ => thrownew InvalidOperationException($"Unknown type: {type}")
        };
    }
}

每加一个新的适配器,就要在工厂里加一行 case。19 种操作 × 5 个工厂方法 = 近百行 switch-case。而且这些代码纯粹是样板,没有任何业务逻辑。

AntFlowCore 的作者选择了一个不同的解法:用 Natasha 在启动时动态编译出这个工厂类。

具体做法分三步:

第一步,在接口方法上打标记:

publicinterfaceIAdaptorFactory
{
    [SpfService(typeof(IProcessOperationAdaptor))]
IProcessOperationAdaptor GetProcessOperationAdaptor(string bizObjectCode);

    [AutoParse]
IEnumerable<TAutoParseAdaptors<T>(string tag);
}

第二步,启动时通过反射读取这些标记,生成 C# 源码字符串。

第三步,用 Natasha 的 AssemblyCSharpBuilder 编译并实例化:

var builder = new AssemblyCSharpBuilder();
builder.AddCode(generatedSource);
var assembly = builder.Compile();
var factory = (IAdaptorFactory)assembly.CreateInstance("GeneratedAdaptorFactory");

最终运行时拿到的是一个编译好的、类型安全的工厂类,性能跟手写的 switch-case 一模一样,但代码是自动生成的。

好代码不写在 switch-case 里,而是让编译器替你写。

这个模式的价值不在于"省了几行代码",而在于它建立了一个零维护成本的扩展机制:新增适配器只需要实现接口并注册到 DI,工厂自动感知,不需要改任何已有代码。开闭原则,落地得相当漂亮。

图片

3.2 Rougamo.Fody AOP 事务:告别手动 Begin/Commit/Rollback

工作流引擎对事务的要求极高——一个审批动作涉及创建任务、更新执行上下文、写入历史记录、发送通知,任何一步失败都要全部回滚。

传统的写法:

publicasync Task DoSomething()
{
usingvar uow = _freeSql.CreateUnitOfWork();
try
    {
// 业务逻辑...
        uow.Commit();
    }
catch
    {
        uow.Rollback();
throw;
    }
}

每个需要事务的方法都要写一遍。AntFlowCore 的做法是用 Rougamo.Fody 做 IL 编织,把事务逻辑抽成一个 TransactionalAttribute

[Transactional]
publicasync Task DoSomething()
{
// 只写业务逻辑,事务自动管理
}

编译时,Fody 会把方法的 IL 织入成类似这样的结构:进入方法时开始 UnitOfWork,正常退出时 Commit,抛出异常时 Rollback。还支持异步方法。

这是 .NET 生态一个被低估的用法。AOP 在 Java 的 Spring 里是标配,但在 .NET 里很多人还停留在"写个拦截器"的阶段。Rougamo.Fody 的 IL 编织方案,性能比动态代理更好——因为它发生在编译时,运行时零开销。


3.3 19 种流程操作:策略模式的教科书级应用

一个成熟的工作流引擎,核心能力体现在"审批过程中能做什么"。AntFlowCore 定义了 19 种流程操作,每种都是一个独立的 IProcessOperationAdaptor 实现:

操作
场景
Submit
提交流程
Resubmit
打回后重新提交
OutSideAccessSubmit
外部系统提交审批
End
终止流程
ChangeAssignee
变更当前审批人
TransferAssignee
转办给他人
BackToModify
打回至任意节点修改
ProcessForward
向前推进
Undertake
承办(接收被委派的任务)
AddAssignee
加签(动态增加审批人)
RemoveAssignee
减签(动态移除审批人)
AddFutureAssignee
修改未来节点审批人(增加)
RemoveFutureAssignee
修改未来节点审批人(移除)
ChangeFutureAssignee
修改未来节点审批人(变更)
FastForward
快速前进
RemoveCurrentNode
移除当前节点
RemoveFutureNode
移除未来节点
InsertNodeAfterCurrentOrFuture
在节点后插入新节点
TaskRecover
任务恢复

所有操作统一走 ButtonsOperation 入口,通过策略模式分发到对应的适配器。API 层只需要一个端点:

[HttpPost("process/buttonsOperation")]
public Result<BusinessDataVo> ButtonsOperation(
    [FromServices] IHttpContextAccessor accessor,
    [FromQuery] string formCode
)

{
var values = accessor.HttpContext!.ReadRawBodyAsString();
return _processApprovalService.ButtonsOperation(values, formCode);
}

一个端点,19 种行为。这也是为什么中国企业的审批场景能被覆盖得这么完整——每种操作都是一个独立的扩展点,而不是硬编码在引擎里的固定流程。


3.4 模板方法模式:表单操作的生命周期

除了流程操作,表单数据处理也是一个核心问题。AntFlowCore 用 IFormOperationAdaptor<T> 接口定义了 9 个生命周期方法:

publicinterfaceIFormOperationAdaptor<T>
{
voidPreviewSetCondition(T vo);    // 预览时设置条件
voidLaunchParameters(T vo);       // 启动参数
voidOnInitData(T vo);             // 初始化数据
voidOnQueryData(T vo);            // 查询数据
voidOnSubmitData(T vo);           // 提交数据
voidOnConsentData(T vo);          // 同意时处理
voidOnBackToModifyData(T vo);     // 打回修改时处理
voidOnCancellationData(T vo);     // 撤销时处理
voidOnFinishData(BusinessDataVo vo);   // 流程完成时处理
}

这是一个经典的模板方法模式。每种表单类型实现这个接口,引擎在流程的不同阶段自动调用对应的方法。

开发者添加一个新表单类型只需要做两件事:

[DIYFormServiceAnno(SvcName = "LeaveApply", Desc = "请假申请")]
publicclassLeaveApplyFlowService : IFormOperationAdaptor<LeaveApplyVo>
{
publicvoidOnSubmitData(LeaveApplyVo vo) { /* 创建请假记录 */ }
publicvoidOnConsentData(LeaveApplyVo vo) { /* 更新请假状态 */ }
}

然后注册到 DI。不需要修改引擎代码,不需要改路由,不需要改任何已有文件。FormFactory 通过 [DIYFormServiceAnno] 属性自动发现并路由到正确的适配器。

图片

四、工作流能力的深度

评估一个工作流引擎,不能只看它支持多少种节点类型,更要看它在审批过程中能做什么

AntFlowCore 的审批能力清单:

  • 顺序会签

    审批人依次审批,前一个不同意后一个看不到
  • 并行会签

    所有审批人同时审批,需要全部通过
  • 或签

    :多个审批人中任意一人通过即可
  • 加签/加批

    审批过程中动态增加审批人
  • 转办

    把审批任务转给他人处理
  • 委托

    将任务委托给他人代办
  • 打回至任意节点

    不是只能打回到上一个节点,可以指定任意历史节点
  • 撤回

    提交后可以撤回
  • 快速前进

    跳过某些节点
  • 减签

    审批过程中动态移除审批人
  • 未来节点修改

    流程还没走到某个节点时,就可以提前修改那个节点的审批人

其中"未来节点修改"和"打回至任意节点"是比较少见的功能,尤其适合中国企业里常见的"流程走到一半领导说换个人"、"这个申请打回到申请人重新填"等场景。

人员解析策略也很丰富:直属领导、HRBP、角色、职级、循环、业务表关联、自选、发起人、自定义外部接口。基本覆盖了中国企业组织架构的各种变体。

图片

五、一些容易被忽略的细节

5.1 15 个自定义 JSON Converter

AntFlowCore.Base/conf/json/ 目录下藏着 15 个自定义的 System.Text.Json.JsonConverter

为什么需要这些?因为前端传过来的数据并不总是类型一致的。布尔值有时是 true/false,有时是 0/1;字符串有时是单个值,有时是数组。与其在业务代码里到处做类型判断,不如在反序列化阶段统一处理。

这是防御性编程的正确姿势:在系统边界做校验和转换,内部代码保持干净。

5.2 View Object 扁平列表模式

BPMN 模型通常用树或图结构表示,但 AntFlowCore 在运行时用一个扁平的 BpmnConfCommonElementVo 对象列表,通过 FlowFrom / FlowTo 字段链接节点。BpmnNodeFormatService(557 行)负责将设计器的树形结构转换为这个扁平格式,后续的流转逻辑只需要在列表中按链接查找即可。

5.3 多租户内置支持

每个实体都有 TenantId 字段,MultiTenantIdHolder 通过 ThreadLocalContainer 存储当前租户信息,在 HTTP 中间件中设置。不需要在业务代码中显式传递租户 ID。

图片

六、客观评价:优点和不足

优点

架构干净。 11 个项目的依赖方向清晰,没有循环依赖,没有跨层调用。这种分层不是一开始就规划好的,而是随着功能增加不断重构出来的。

扩展性设计到位。 新增表单类型、条件判断器、通知渠道、流程操作,都是"实现接口 + 注册 DI"两步完成,不需要修改已有代码。开闭原则不是口号,是落地的设计。

技术选型有想法。 Natasha 动态工厂和 Rougamo.Fody AOP 都不是 .NET 生态的主流选择,但在这个场景下用得很恰当。说明作者不是在堆技术,而是在解决实际问题时选择了合适的工具

对中国企业场景的理解深刻。 顺序会签、加签、打回至任意节点、未来节点修改——这些功能不是从 BPMN 规范翻译过来的,而是从钉钉的实际产品需求反推出来的。

不足

没有自动化测试。 整个解决方案找不到一个测试项目。对于一个工作流引擎来说,这是硬伤。19 种流程操作、22 个事件钩子、9 个生命周期方法——没有测试覆盖,重构和升级时谁来保证正确性?

文档多但缺少入门指南。 项目里有 15+ 份技术文档,内容很详细,但缺少"从零到一跑起来"的入门教程。对于想快速上手的人来说,学习曲线偏陡。

数据库脚本齐全但缺乏迁移工具。 提供了 MySQL、Oracle、PostgreSQL、SQL Server 的初始化脚本,但没有集成数据库版本管理工具。生产环境升级时的数据库变更需要手动处理。

示例代码可以更多。 只有一个 ThirdPartyAccountApplyFlowService 示例,对于一个面向开发者的引擎来说,一两个示例不太够。


七、Java 版 vs .NET 版:不是翻译,是重新解题

很多人会问:.NET 版跟 Java 版有什么区别?只是语法翻译吗?

不是。 两者的核心业务流程和前端设计器是相同的,但实现方式上有明显差异:

  • Java 版用 Spring 的依赖注入和 AOP,.NET 版用原生的 DI + Rougamo.Fody
  • Java 版用 MyBatis/JPA,.NET 版用 FreeSql
  • .NET 版独有的 Natasha 动态工厂是 Java 版没有的设计
  • .NET 版直接面向 .NET 10 / C# 13,用到了最新语言特性

换句话说,.NET 版不是对 Java 版的逐行翻译,而是用 .NET 生态的工具链重新实现了同一套业务能力。 这比直接移植要有价值得多——因为它真正融入了 .NET 的技术栈,开发者可以用 .NET 的方式去理解和扩展它。

图片

八、适合什么场景?能学到什么?

适用场景

如果你的项目需要审批流功能,AntFlowCore 适合以下场景:

  • 企业内部系统

    OA、ERP、CRM 中需要审批流程的模块
  • 低代码/无代码平台

    作为底层工作流引擎嵌入
  • 需要钉钉风格审批流的项目

    顺序会签、加签、转办等中国式审批
  • 多租户 SaaS 产品

    内置多租户支持

不太适合的场景:

  • 需要严格 BPMN 2.0 兼容的项目(这是类 BPMN,不是标准实现)
  • 需要与 Java 生态微服务深度集成的场景
  • 对自动化测试有强制要求的企业(需要自己补测试)

学习价值

不管你是否会在项目中使用它,AntFlowCore 的源码都值得翻一翻:

  1. Natasha 动态代码生成

    一个"让编译器替你写样板代码"的完整实现
  2. 策略模式的大规模应用

    19 种操作的统一入口和分发机制
  3. AOP 事务的 IL 编织实现

    比动态代理性能更好的 .NET AOP 方案
  4. BPMN 引擎的实现思路

    流程定义、执行上下文、任务流转
  5. 自定义 JSON Converter 的防御性设计

    在系统边界处理类型不一致

写在最后

开源社区从来不缺工作流引擎,但缺的是认真对待工程细节的引擎。

AntFlowCore 最值得认可的不是它实现了多少功能,而是它在每个技术决策上展现出的克制和品味:用 Natasha 消除样板代码而不是堆砌 switch-case,用 AOP 管理事务而不是在每个方法里写 try-catch,用 15 个 JSON Converter 在前端边界做防御而不是在业务逻辑里到处判断类型。

移植不是翻译,而是用新语言的工具重新解题。 这一点,AntFlowCore 做到了。

当然,没有自动化测试是实实在在的短板。但考虑到这个项目 2024 年 11 月才起步,作为一个个人主导的开源项目,能做到现在的完成度,已经值得关注。

如果你正在为 .NET 项目选型工作流引擎,或者单纯想看看一个认真做的 .NET 开源项目长什么样,不妨去翻翻它的源码。

项目地址:

https://github.com/mrtylerzhou/AntFlow.net



群贤毕至

访客