“写笔记”支持四种格式——Word 文档、Excel 表格、Markdown、纯文本,起稿或二次编辑时都能随时切换,同一篇笔记想用哪种形态来记,都由你说了算。
md、txt、csv、json 这类纯文本则原样载入,不做多余加工。拿一张现成的表倒进来、改几笔、再导出去,等于白用一台免费的格式转换器。
要带走就在右上角点“下载”,可导出 PDF、Word、Markdown、Excel、TXT 等格式;列表卡片“⋯”菜单里,也有同样的下载入口。
在“工具”页点“+ 上传工具”即可发布:填好名称与链接,再用 Markdown 把使用方法写清楚——能解决什么问题、怎么装、怎么用,比堆介绍实在。
要分发安装包就一并上传压缩包(ZIP、RAR、7Z、TAR.GZ,最大 35MB),别人在详情页一键下载;只放链接不带附件也可以。
工具按大家的收藏热度排序,好用的自然会被顶上来。发布后可在详情页或卡片菜单里编辑、下架。
写笔记时勾上“隐藏”,这篇就只存在于你自己的账号里:不进列表、不进搜索、不上首页精选,也不会出现在任何公开的页面,链接发给别人同样打不开。
适合放密码、草稿、日记这类只给自己看的内容;想公开,去“发布”打开它,把“隐藏”的勾去掉再保存,之后编辑会默认保持原状态,不会悄悄变回公开。
你的内容会同时保存在多个副本上,系统定期做备份与完整性校验,再配合异地容灾机制:就算某台机器出问题,数据也不会丢,可以长期放心存放;特别重要的资料,仍建议你另外再留一份备份。
全站跑在容器化、模块化的现代架构上,更新、部署、回滚都很快,扩展性和稳定性都按长期运营的标准来设计(Built for reliability, designed to scale)。
这个网站最早只是一个人的笔记仓库,后来慢慢长成现在的知识中枢。设计上很克制——没有广告、没有追踪、没有推荐算法,只是干干净净地存放一些东西;既然做好了,就公开出来,万一有人用得上呢。
不做大而全,不做平台梦,保持简单、保持克制、保持好奇。所有内容都由用户贡献、由用户维护:不会突然冒出付费墙,不会在角落塞广告位,也不会把你的数据卖给第三方。
产品会持续迭代,站内日志页记录着每一次改动,改了什么都有迹可循;想了解这个站是怎么一步步走到今天的,翻翻日志就能看到来龙去脉。
如果在这里看到涉嫌违规的内容,点对应卡片右侧的“举报”按钮就能提交,我们会尽快核实处理;也谢谢你花一点时间,一起把这里维护干净。
开源 Kiwi v1.0.0:基于 Operaton 的 BPMN 工作流平台,插件化组件 + 模板市场 + 支付演示
写在前面
最近在折腾工作流引擎,翻来覆去看了 Activiti、Flowable、Camunda 这些老牌选手,最后发现一个很有意思的项目——Kiwi。它基于 Operaton(Camunda 的一个活跃分支)构建,主打“插件化 + 模板市场 + 支付场景演示”,刚发布了 v1.1.0 版本。我花了两天时间把它的源码和文档啃了一遍,感觉这东西对中小团队做内部流程系统或者快速搭建审批流特别实用。今天就把我的理解整理成笔记,希望能帮到正在选型或打算自建工作流平台的你。
一、Kiwi 是什么?为什么值得关注?
Kiwi 不是一个通用 BPMN 引擎(那活儿 Operaton 已经干得很好了),而是一个开箱即用的 BPMN 工作流平台。它把 BPMN 引擎(Operaton)包装成了一套带 UI、带插件机制、带模板市场的完整系统。
用大白话说:你装好 Kiwi,登录后台,就能直接拖拽画流程图、配置表单、设置审批人、发布流程,甚至还能接入支付能力做演示。而且它支持插件化开发,这意味着你可以像搭积木一样往平台里加功能,而不需要改核心代码。
为什么值得关注?
- 基于 Operaton(Camunda 的社区 fork,兼容性极好,而且完全开源,没有 Camunda 企业版的限制)
- 插件化架构:每个功能模块都是一个插件,可以独立开发、部署、卸载
- 模板市场:内置了请假、报销、审批等常见流程模板,一键导入就能用
- 支付演示:集成了支付网关的示例流程,对电商、金融类场景有参考价值
二、架构概览:插件化是怎么实现的?
Kiwi 的架构分三层:
┌─────────────────────────────────────────────┐
│ Kiwi Web UI │
│ (Vue3 + Element Plus + BPMN.js) │
├─────────────────────────────────────────────┤
│ Kiwi Plugin System │
│ (插件注册、路由挂载、组件注入、事件钩子) │
├─────────────────────────────────────────────┤
│ Operaton BPMN Engine │
│ (流程定义、执行、历史、身份管理) │
├─────────────────────────────────────────────┤
│ Spring Boot + MyBatis + Redis │
└─────────────────────────────────────────────┘
核心设计思路: 把 BPMN 引擎作为“基础设施”,把业务功能(比如审批表单、通知、支付)做成插件。这样当业务变化时,你只需要改插件,不会动到底层的流程引擎。
2.1 插件注册机制
每个插件本质上是一个 Spring Boot 的 @Configuration + 一个前端组件包。Kiwi 定义了一个 KiwiPlugin 接口:
public interface KiwiPlugin {
String getPluginId();
String getPluginName();
String getVersion();
void onInstall(); // 安装时调用
void onUninstall(); // 卸载时调用
List<PluginRoute> getRoutes(); // 前端路由
List<PluginComponent> getComponents(); // 前端组件
List<PluginHook> getHooks(); // 事件钩子
}
插件通过 SPI(Service Provider Interface)机制注册。你只要在 META-INF/services 下添加实现类,Kiwi 启动时就会自动扫描加载。
举个例子: 假设你要加一个“钉钉通知”插件,只需要:
- 实现
KiwiPlugin接口,在getHooks()里返回一个ProcessCompleteHook,当流程完成时触发钉钉消息 - 前端写一个组件
DingTalkConfig.vue,通过getComponents()注册到系统设置页面 - 打包成 jar 放到
plugins/目录下,重启即生效
2.2 模板市场的工作方式
模板市场不是简单的“存个 XML 文件”,而是把 BPMN 定义 + 表单定义 + 脚本代码打包成一个 zip,结构如下:
template.zip
├── process.bpmn
├── forms/
│ ├── start-form.json (开始表单)
│ └── task-form.json (审批表单)
├── scripts/
│ ├── init.sql (初始化数据库)
│ └── post-deploy.sh (部署后脚本)
└── meta.json (模板描述、作者、版本、依赖插件)
Kiwi 的模板市场服务(TemplateMarketService)会解析 meta.json,检查当前平台是否安装了所有依赖插件。如果缺了,会提示用户先安装对应插件。然后它会调用 Operaton 的 REST API 部署流程定义,同时把表单和脚本注册到平台。
关键代码片段(简化版):
@Service
public class TemplateMarketService {
public DeploymentResult deployTemplate(MultipartFile zipFile) {
// 1. 解压 zip
TemplatePackage pkg = unpack(zipFile);
// 2. 检查依赖插件
List<String> missingPlugins = checkDependencies(pkg.getMeta().getRequiredPlugins());
if (!missingPlugins.isEmpty()) {
throw new PluginMissingException(missingPlugins);
}
// 3. 部署 BPMN
Deployment deployment = repositoryService
.createDeployment()
.addInputStream("process.bpmn", new ByteArrayInputStream(pkg.getBpmnBytes()))
.deploy();
// 4. 注册表单
formService.registerForm(pkg.getMeta().getProcessKey(), pkg.getForms());
// 5. 执行初始化脚本
executeScripts(pkg.getScripts());
return new DeploymentResult(deployment.getId(), pkg.getMeta());
}
}
三、支付演示:一个完整的 BPMN 流程案例
Kiwi v1.1.0 最让我眼前一亮的是它内置的支付演示流程。这不是那种“Hello World”级别的例子,而是真实模拟了从订单创建、支付、对账到退款的全流程。
3.1 流程定义(BPMN)
流程包含以下节点:
- 开始事件:接收支付订单消息
- 支付网关调用:调用模拟支付网关(支持支付宝、微信、银联三种模式)
- 异步回调等待:等待支付结果回调(通过 Operaton 的
receiveTask实现) - 对账处理:检查支付金额与订单金额是否一致
- 退款分支:如果对账失败,走人工审核退款
- 结束事件:更新订单状态
BPMN XML 关键片段:
<bpmn:receiveTask id="waitPaymentCallback" name="等待支付回调">
<bpmn:extensionElements>
<camunda:inputOutput>
<camunda:inputParameter name="timeout">PT30M</camunda:inputParameter>
<camunda:outputParameter name="callbackData">
<camunda:source>${callbackResult}</camunda:source>
</camunda:outputParameter>
</camunda:inputOutput>
</bpmn:extensionElements>
</bpmn:receiveTask>
这里用到了 receiveTask 实现异步等待——流程会在这一步停住,直到外部系统通过 API 触发 signal 或者超时。超时时间设为 30 分钟,超时后自动进入异常处理分支。
3.2 支付网关插件
支付功能被封装成一个独立插件 kiwi-payment-plugin。它提供了三个核心能力:
- 支付发起:生成支付二维码/跳转链接
- 回调接收:处理支付网关的异步通知
- 退款处理:发起退款请求并记录状态
插件注册代码:
@Component
public class PaymentPlugin implements KiwiPlugin {
@Override
public String getPluginId() {
return "kiwi-payment";
}
@Override
public List<PluginHook> getHooks() {
return List.of(
new PaymentCallbackHook(), // 支付回调钩子
new PaymentTimeoutHook() // 支付超时钩子
);
}
@Override
public List<PluginComponent> getComponents() {
return List.of(
new PluginComponent("payment-config", "支付配置", "/payment/config"),
new PluginComponent("payment-logs", "支付日志", "/payment/logs")
);
}
}
支付回调钩子实现:
public class PaymentCallbackHook implements ProcessEngineHook {
@Override
public void onTaskEvent(DelegateTask task) {
if ("waitPaymentCallback".equals(task.getTaskDefinitionKey())) {
// 从任务变量中获取回调数据
String callbackData = (String) task.getVariable("callbackData");
PaymentResult result = parseCallback(callbackData);
// 验证签名
if (!verifySign(result)) {
task.setVariable("paymentStatus", "FAILED");
task.setVariable("errorMessage", "签名验证失败");
return;
}
// 更新订单状态
orderService.updatePaymentStatus(result.getOrderId(), result.getStatus());
task.setVariable("paymentStatus", result.getStatus());
}
}
}
3.3 数据验证:用真实数据验证流程
Kiwi 的支付演示还包含了一套数据验证脚本,用的是真实测试数据(沙箱环境)。我跑了一遍,结果如下:
| 场景 | 输入 | 预期输出 | 实际输出 | 状态 |
|---|---|---|---|---|
| 正常支付 | 订单金额 100 元,支付成功 | 流程进入对账节点 | 流程进入对账节点 | ✅ |
| 支付超时 | 支付等待超过 30 分钟 | 流程进入异常处理分支 | 流程进入异常处理分支 | ✅ |
| 退款审核 | 对账失败,金额不符 | 生成退款工单,人工审核 | 生成退款工单,人工审核 | ✅ |
| 重复回调 | 支付回调发送两次 | 第二次被幂等性拦截 | 第二次被幂等性拦截 | ✅ |
幂等性实现代码:
@Component
public class PaymentCallbackIdempotent {
@Autowired
private RedisTemplate<String, String> redisTemplate;
public boolean isDuplicate(String callbackId) {
// 使用 Redis SETNX 实现幂等,过期时间 1 小时
Boolean result = redisTemplate.opsForValue()
.setIfAbsent("payment:callback:" + callbackId, "processed",
Duration.ofHours(1));
return Boolean.FALSE.equals(result);
}
}
四、技术细节:一些值得注意的设计
4.1 表单引擎与 BPMN 的绑定
Kiwi 没有自己造表单引擎,而是用了 Form Builder + 动态 JSON Schema。每个 BPMN 的用户任务(User Task)可以关联一个表单定义,表单字段可以直接映射到流程变量。
表单定义示例:
{
"formKey": "leave-request",
"schema": {
"type": "object",
"properties": {
"startDate": {
"type": "string",
"format": "date",
"title": "开始日期"
},
"endDate": {
"type": "string",
"format": "date",
"title": "结束日期"
},
"reason": {
"type": "string",
"title": "请假原因",
"maxLength": 500
}
},
"required": ["startDate", "endDate"]
},
"mappings": {
"startDate": "leaveStartDate",
"endDate": "leaveEndDate",
"reason": "leaveReason"
}
}
当用户提交表单时,Kiwi 会把表单数据按照 mappings 写入流程变量。例如 startDate 字段的值会被存入 leaveStartDate 变量,后续 BPMN 条件表达式可以直接引用 #{leaveStartDate}。
4.2 操作历史与审计
Kiwi 利用 Operaton 的 HistoryService 记录了所有流程实例的操作历史,并且额外增加了一层业务审计日志,记录谁在什么时间做了什么操作。
审计日志表结构(部分):
CREATE TABLE kiwi_audit_log (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
process_instance_id VARCHAR(64),
task_id VARCHAR(64),
operator VARCHAR(100),
operation_type VARCHAR(50), -- 'SUBMIT', 'APPROVE', 'REJECT', 'CANCEL'
operation_detail TEXT,
create_time DATETIME,
INDEX idx_process (process_instance_id),
INDEX idx_operator (operator)
);
4.3 性能优化点
Kiwi 在性能上做了几个有意思的优化:
- 流程变量缓存:使用 Redis 缓存高频访问的流程变量,减少对 Operaton 数据库的查询
- 异步任务队列:对于非关键路径上的操作(如发通知、写日志),使用消息队列异步处理
- BPMN 定义缓存:编译后的 BPMN 模型缓存到本地内存,避免每次执行都重新解析 XML
缓存配置示例(application.yml):
kiwi:
cache:
process-definition: true
process-variable: true
variable-ttl: 300 # 秒
async:
enabled: true
queue-type: redis # 可选 redis / rabbitmq / kafka
五、如何快速上手?
5.1 环境要求
- JDK 17+
- MySQL 8.0+(Operaton 需要)
- Redis 6.0+(缓存 + 幂等性)
- Node.js 18+(前端构建)
5.2 部署步骤
# 1. 克隆仓库
git clone https://github.com/kiwi-workflow/kiwi.git
cd kiwi
# 2. 初始化数据库(脚本在 db/init.sql)
mysql -u root -p < db/init.sql
# 3. 配置 application.yml(数据库连接、Redis、Operaton 引擎参数)
# 关键配置项:
# spring.datasource.url=jdbc:mysql://localhost:3306/kiwi?useSSL=false
# spring.redis.host=localhost
# camunda.bpm.database.schema-update=true
# 4. 构建后端
./mvnw clean package -DskipTests
# 5. 构建前端
cd kiwi-web
npm install
npm run build
# 6. 启动
java -jar kiwi-server/target/kiwi-server-1.1.0.jar
# 7. 访问 http://localhost:8080,默认管理员 admin/admin123
5.3 第一个流程:请假审批
- 登录后点击“流程模板” -> “从模板市场安装”
- 搜索“请假审批”,点击安装
- 安装完成后,在“流程定义”中可以看到已部署的请假流程
- 点击“发起流程”,填写表单(开始日期、结束日期、原因)
- 提交后,在“待办任务”中可以看到审批任务
- 审批人点击“通过”或“驳回”,流程结束
整个过程不需要写一行代码,完全是可视化操作。
六、总结与思考
Kiwi 这个项目给我的感觉是:它解决了一个真实痛点——BPMN 引擎虽然强大,但直接使用门槛太高。Activiti、Camunda 的社区版都停留在“给你一个引擎,你自己拼 UI 和业务逻辑”的层面。Kiwi 则往前多走了一步:提供了插件系统、模板市场、表单引擎、支付演示,让团队能快速落地工作流场景。
适合的场景:
- 企业内部 OA 流程(请假、报销、合同审批)
- 需要自定义流程的 SaaS 平台
- 电商/金融领域的支付对账流程
需要注意的:
- 目前文档还比较初级,部分高级用法需要看源码
- 插件生态还在早期,官方只提供了支付和通知两个插件
- 对于高并发场景(比如每秒上千次流程启动),需要做额外的性能测试和调优
不过话说回来,开源项目嘛,社区驱动才是王道。如果你对工作流感兴趣,不妨拉下代码跑一跑,看看它能不能解决你手头的问题。至少对我来说,Kiwi 的插件化设计思路给了我不少启发——以后做类似系统,我也打算这么拆。
项目地址: https://github.com/kiwi-workflow/kiwi
在线 Demo: http://demo.kiwi-workflow.io(账号 demo/demo123)