把下面十三张工作台截图摆在一起,你分不出哪一台手机连的是 Java,哪一台连的是 Rust。
因为它们本来就是同一个 App。
今天,这个项目的 Android 体验包和全部源码一起开源了。这篇文章讲清楚三件事:它能干什么,"十三套后端随便换"是怎么做到的,以及你什么时候会需要它。
一、先装上,三分钟批完一条请假单
不用装任何开发环境,一个安装包就够:
- 打开 www.pgyer.com/jeeflowshen...(蒲公英),扫码或直接下载安装,桌面名叫「jeeflow审批」;
- 打开 App,它默认连接线上演示站,不需要任何配置;
- 用演示账号
admin/123456登录。
登录后就是工作台:待办和消息的角标、四格小指标(待办/已办/我发起/抄送)、按业务分类的快捷发起宫格。点进待办里的一条请假单,你会看到两块内容:上面是申请内容(请假类型、天数、事由------表单字段是动态的,跟着流程定义走),下面是流程图。


这张流程图值得多说一句:灰色虚线是还没走的路,已走过的节点和边会高亮,当前停在哪一步一目了然,条件分支("≤3天仅部门审批 / >3天再分管领导")直接画在图上。批完这条单子,它就从待办流进已办,发起人那边的实例状态同步流转------一套完整审批闭环,你刚才在手机上三分钟走完了。
除了"同意/不同意",待办里能做的动作还有撤回、委托代理设置、抄送查阅、消息提醒,Web 端审批人的日常操作,这里全量对齐。你批过的每一个动作也都留得住痕迹:实例详情里有完整的审批时间线------谁、什么时候、给了什么意见;消息中心会收到单据动态;发起人那边实时看得到状态变化。审批不是点一个按钮就结束的黑箱,而是一条可回溯的链路。
二、和 Web 端是同一套流程,不是另一个系统
很多"移动审批"的实现是给某几个固定流程手写几个页面。这个 App 不是,它和 Web 管理端消费的是同一份流程定义、同一套表单分发逻辑:
- 流程在 Web 端设计器里画好、发布,手机端工作台的快捷发起里立刻出现;
- 发起表单是动态渲染的:流程定义声明了表单标识,App 按标识找本地注册的自定义组件(比如带联动计算的请假表单),找不到就走通用 Schema 表单按元数据渲染列,再不行就明确报"未注册",绝不偷换一张错误的表单给你;
- 审批时看到的流程图、时间线、审批意见,和 Web 端是同一套数据端点,同一个前端组件库(此前文章介绍过的钉钉风格流程设计器,手机端用的就是它的查看器)。

统计分析页也是个"不是 mock"的例子:指标卡、趋势(近7天/30天/12月)、状态分布、流程 Top 10、当前积压节点,全部来自工作流引擎的统计接口真数据------下面的截图里连"平均办结 29 小时、驳回率 17.2%"这种质量指标都是引擎算出来的。

一句话:流程还是那条流程,手机只是多了一块屏幕。
三、十三套后端,一套 App
接下来是这篇文章真正想说的部分。
这个 App 服务的后端,是同一个快速开发框架家族的十三个语言实现:Java 三个版本(Spring Boot 2/3/4)、Go 三个(GoFrame/Gin/Hertz)、Python 三个(FastAPI/Flask/Django),再加上 Node.js(NestJS)、PHP(Laravel)、Rust(Salvo)和 C#(ASP.NET Core)。每个实现都有一个集成工作流引擎的版本,各自是一套独立可部署的完整系统。
App 只认契约,不认实现。所有请求是统一的形态:/sys/** 管认证、用户、字典、消息,/wf/{action} 一个单门面走完工作流的四十多个动作(App 用其中约三十个);响应统一 {code, msg, data},雪花 id 全程按字符串传递。从第一行代码起,App 就是照着契约写的,里面没有任何一处 if (后端是某某语言)。
话不能只说到这里------"理论上兼容"和"逐栈跑通"是两回事。所以在开源前,我们做了一轮交叉验收:官网一键脚本把十三个栈逐个部署成干净的演示环境,每个栈上跑一遍对齐 App 真实调用面的契约冒烟(登录、用户、列表、分页、消息、统计......十二项硬门),再走一遍完整的请假闭环(发起 → 待办 → 审批 → 完结),最后换真机逐栈登录把 UI 过一遍。
正因为部署单个栈只需要一条命令,验收才能像流水线一样跑:起一个栈、测一轮、拆掉、换下一个,十三轮下来留下十三份逐栈检查记录。
结果:13/13 全绿。下面的拼图不是同一张截图复制十三份------是同一台模拟器连十三个不同后端逐栈截的:

这里有个小故事。十三个栈的种子数据完全一样,工作台长得一模一样,截图发出来没法自证"我真的连到了这个栈"。解法是登录前先用 API 在当前栈悄悄发起一条请假:待办从 9 变成 10,截图里的「待办 10」就是指纹------上面七张图显示 10,另外六张显示 9,是因为那几个栈的探针单子刚好被完整链路走查批掉了。
当然,十三套实现也不是像素级铁板一块,真实的差异长这样:
- 有的栈把比率字段序列化成字符串
"0.1481",有的给数字0.1481; - 分页信封里"总条数"的字段名,多数栈一个叫法,个别栈另一个叫法;
- 个别统计字段在没有数据时会返回 null,需要兜底显示。
这些差异全部被吸收在 App 的几个兼容函数里------字段级的小适配,不是按语言分叉的调用逻辑。反过来说,这也是对"契约对齐"工程的一次实弹检验:契约写得够细,十三套实现就能被一个客户端无感消费;漏掉的地方,客户端会诚实地暴露出来。
四、为什么是 uni-app x
技术选型一句话讲完:uni-app x ,页面用 .uvue、逻辑用 .uts,编译产物是原生应用------不是 WebView 套壳。客户端工程本身不挂 npm 依赖树,唯一的"网页"是流程图那一页,用的也是预先构建好的组件文件。
多提一句这个选型的背景:uni-app x 是 DCloud 押注的编译到原生的跨端新方案,落地时间还不长,网上可参考的完整开源项目并不多。这个仓库体量不大,但从登录、动态表单、列表流、图表页到原生模块集成五脏俱全,想入坑 uni-app x 的开发者,可以把它当一份能跑起来的实战参考。
几个和使用体验直接相关的点:
- 多后端切换:开发环境下长按登录页 logo(真机上摇一摇也行)会弹出接口地址切换,预置的档位可以自由增删------十三个栈就是这么逐个连过去测的。正式打包则锁定线上地址,普通用户看不到这个入口。
- 会话续命:登录拿到的一对令牌里,短命的过期时 App 不会把你踢回登录页,而是拿长命的静默换一对新的,然后把你刚才那次请求原样重发------你只会感觉"怎么还没过期"。换新也失败了(比如真的很久没来),才回登录页。这是配合后端 refreshToken 全量轮转机制做的移动端落地。
- 原生渲染:列表滚动、角标、手势都是原生视图,这在自绘渲染的框架里不算稀奇,但它是编译期类型检查的------写错类型直接编译不过,运行时少一整类低级崩溃。
开发过程中踩的坑也不少(自绘 UI 进不了系统控件树、自动化测试只能靠坐标点、原生模块要用自定义基座重新打包......),足够单开一篇,这里按下不表。
五、开源信息
- GitHub 仓库 :github.com/mldong/uni-...(Apache 2.0)
- 文档与从源码运行 :jeeflow-doc.mldong.com/guides/14-u...(HBuilderX + 自定义基座,半小时内可跑起来)
- Android 免编译体验 :www.pgyer.com/jeeflowshen...(演示账号 admin / 123456)
- 在线演示 :开源演示站 jeeflow-demo.mldong.com · 集成演示站 jeeflow-pro.mldong.com
- 工作流引擎 jeeflow :jeeflow-doc.mldong.com(多语言嵌入式工作流引擎,往期系列文章均在文档站可溯)
最后回应一下标题里没写完的半句:如果你也在维护自己的框架或者中后台系统,想给它配一个移动审批端,这个项目的答案也许可以抄------先把契约层立起来,客户端只认契约。至于十三套后端是负担还是资产,取决于它们是否真的被同一个客户端验证过;我们的经验是,验收跑完那一刻,你对"统一契约"这四个字的信心会完全不一样。
如果这个项目对你有用,去 GitHub 点个 star 就是最好的支持;有问题欢迎提 issue,移动端视角的后端兼容问题尤其欢迎。