当前位置: 首页 > news >正文

我一个人写了个通用审批引擎,能对接所有服务

我做后端这几年,见过太多审批系统。有的把审批逻辑硬编码在业务代码里,改一个流程要发版;有的买了第三方的,配置复杂到产品经理都看不懂。去年我开始搭自己的微服务体系,第一个想的不是认证、不是权限——而是「审批」这个事能不能抽出来,做成一个独立服务,不管哪个业务都能接进来。

半年过去了,审批引擎跑起来了。今天跟你聊聊我是怎么想的、怎么做的,以及中间踩的几个坑。


不是所有审批都一样

刚开始我想得很简单:一张申请表、一个审批人、通过或驳回。但真正开始做了才发现,审批的复杂度不在「通过/驳回」,而在「不同业务长得完全不一样」。

权限申请要展示权限名称和理由,博客审核要展示文章标题和内容预览,考试申请要展示考试名称和时间。如果给每种业务写一套展示逻辑,那这个审批引擎就废了——每次接新业务都要改代码。

我的解法:Schema 快照。

每套审批模板定义一份 form_schema,描述这个业务有哪些字段、字段叫什么、怎么展示:

{"fields":[{"key":"permissionName","label":"申请的权限","type":"text"},{"key":"reason","label":"申请理由","type":"textarea"}]}

创建审批单的时候,把当时的 schema 拍一个快照存进审批实例。以后不管模板怎么改,这个审批单就按创建时的 schema 展示,互不影响。

这个设计后来被验证了好几次——模板改了十几版,历史审批单一个没乱。


第一个坑:申请人不能审批自己

这是测试的时候发现的。我自己提了个权限申请,跑到「待我审批」里一看,居然能看到自己的申请,点了「通过」居然还能通过。

这要是在生产环境,权限就变成自己给自己发了。

前后端双重校验。后端查申请人 ID 和当前用户 ID,相等直接抛「不能审批自己的申请」。前端在列表里提前过滤掉,不让按钮出现。两边都做了,就算绕过前端直接调接口也过不去。


第二个坑:回调的可靠性

审批通过之后要回调业务系统——比如权限申请通过了,要调 perm-service 给用户加权限。问题来了:如果回调失败了怎么办?

我见过有的系统直接不管,失败了就失败了,管理员手动补。这在一个独立开发者手上就是噩梦——你可能过了两天才发现权限没加上。

我的方案:重试 3 次,间隔 1 秒,记录状态。

审批通过/驳回后,立刻 POST 到业务系统配置的回调地址。失败就重试,最多 3 次。每次重试的间隔递增。3 次都失败,标记 callback_status = failed,详情页显示一个黄色「通知失败」提示,管理员一眼能看到哪些需要手动处理。

成功了就显示绿色「已通知」,不用管了。


第三个坑:撤回和重新提交

用户提了申请,发现填错了,想撤回。简单:pending 状态的申请直接置为 cancelled。

但重新提交呢?这时候模板的 schema 可能已经改了。如果按新 schema 展示,用户填的是旧字段,就对不上了。

重新提交继承旧的 schema_snapshot。不会因为模板改了就让用户的申请变样。


最后:五张表搞定一切

审批引擎只有五张表:

  • t_approval_template:审批模板(定义流程和 schema)
  • t_approval_node:审批节点(多级审批)
  • t_approval:审批实例(每一条申请)
  • t_approval_record:审批记录(每一步的操作历史)
  • t_user_permission:用户权限(审批通过后写入)

多一级审批就加一个 node,多一种业务就加一个 template。不用改代码。


回头看看

这个审批引擎算不上什么高深的技术,但它是我自己设计、自己实现、自己验证过的。从 schema 快照的思路,到回调重试的机制,到撤回重新提交的处理——每一个决策都是踩了坑以后逼出来的。

独立开发者有个好处:你做的东西不用向任何人妥协。不好用的地方你就改,想不通的地方你就推翻。最后留下来的是什么?是真正经得起推敲的东西。


关于作者:无羡,独立开发者,全栈工程师,专注 AI 应用与微服务架构。

📌 分类:独立开发实战

🔗 个人博客 — 更多技术文章
✨ 开发者福利整理 — 云服务资源汇总
📂 软件工坊 — 我的技术分享
🌐 个人门户 — 独立开发作品全集

http://www.jsqmd.com/news/1151467/

相关文章:

  • AI驱动FDC实战:半导体制造良率提升之路
  • Loop Engineering 入门:Prompt、Harness、Loop 一次讲透
  • Socket
  • 企业AI管理工具 AIHelms,统一管模型控成本算价值!下载
  • 药品销售平台设计与实现开题报告
  • 绕过 Claude Desktop!Affinity MCP 直连 WorkBuddy/Cursor 完整配置指南(Windows)
  • RC 滤波设计原理(运放偏置电压与 AFE 供电场景)
  • 从函数调用到进程替换:使用 execl、CMake 与 PowerShell 构建一个多可执行程序
  • Hive sql 初级题 05
  • 基于 STM32 的羽毛球挥拍速度与击球点分析系统设计与实现
  • 【语言】C++
  • 第一章 必备基础知识
  • DOPE-硫辛酸DOPE-Lipoic Acid|二油酰磷脂酰乙醇胺-硫辛酸DOPE-LA
  • 百度网盘秒传脚本终极指南:3分钟学会永久分享大文件
  • Vikunja 自建任务管理:个人待办和小团队协作够不够用
  • 茶叶加工厂信息化管理系统设计与实现开题报告
  • MySQL Group Replication实战:多主架构的配置、监控与故障切换
  • 全球机器人行业近三年市场增长深度分析报告
  • 【ROS2】vscode配置头文件路径等
  • 模型在狂飙,底座在重构!拆解AI数据库,聊聊数据开发人的下一代技术栈
  • Hackerone-地址栏xss
  • ClickHouse物化视图与Projection的工程化:预聚合策略的性能对比与选型
  • ARK Survival Evolved 集群服务器搭建教程:多地图联通与角色传送配置
  • 前端反调试攻防:5 种主流 bypass 方案对比与油猴脚本自动化实现
  • 紫铜液冷板焊接飞溅怎么破?环形光斑三招搞定
  • LTM4644 四通道并联 16A 输出配置:PCB 布局 3 要点与热仿真验证
  • 终极解决方案:Windows一键访问Linux磁盘的完整指南
  • 附录C 参考文献开源仓库与延伸阅读《程序员自进化与Agent Harness工程》
  • 微型步进电机为什么成为机器人关节的重要驱动部件?
  • SQL 注入之报错型注入