[JitWeb] + [Workflow] 工作流 doHandle 重复提交 — 应用级 Http 拦截器方案

此前我发过 [JitWeb] + [Workflow] 工作流新建提交重复请求导致并发 duplicate 创建业务单据 的 bug 帖(同一操作同一秒内两次 WorkflowSvc/doHandle 均 200 成功、产生 duplicate 单据)。

在平台侧幂等/前端防重修复之前,我们在业务应用里加了一层 Http 拦截器兜底,在此补充实现思路与验证结果,供有同样痛点的项目参考。

方案定位

  • 元素类型interceptors.Http(应用级,无需每个 workflow 单独配置)
  • 拦截范围:仅 HTTP 入口的 workflows.services.WorkflowSvc/doHandle
  • 不替代:平台 doHandle 幂等、JitWeb 提交防重;也不覆盖 Python 服务内直接调用 doHandle
  • 策略:Redis 短时窗口指纹去重(当前 60 秒),重复请求在进业务逻辑前 PermissionError

指纹(简述)

userId + workflowName + handleType + nodeId + rowData.id + 规范化 rowData → SHA256 → Redis incr

rowData 规范化时会:

  • 排除 id、时间戳、附件 uid/url、审批/流程态字段等
  • 按 workflow 关联模型元数据排除主键、Serial、公式字段,子表递归
  • 明细稳定排序,附件只保留 md5 + fileName

用户体验

  • 第一次:正常建单 / 走审批
  • 60 秒内相同内容再次请求:返回「请勿重复提交,请稍候再试」,不再落库
  • Redis/读参异常:fail-open 放行(避免拦截器故障拖垮全部审批)

验证(已做)

  1. 并发重放:Network 复制 doHandle cURL,同一 body 并发 POST 两次 → 一次成功、一次被拦
  2. 已提交后重放:手动提交成功后相同 body 再重放 → 均被拦,列表不新增
  3. 日志[WorkflowSubmitDedup] blocked duplicate doHandle | workflow=... handleType=... nodeId=...

(测试请勿在公开帖粘贴含 token/sign 的 curl 或含 userId/业务数据的 server.log。)

代码结构

interceptors/WorkflowSubmitDedup/

├── e.json # sort: 30, type: interceptors.Http

├── _init_.py

├── interceptor.py # 核心逻辑

└── README.md # 维护说明

核心依赖平台能力:RequestInterceptor.before()self.requestdoHandleargDictcaches.Default(Redis)。

与 bug 帖的关系

层级 状态
平台根因(双发 + 无幂等) 仍待平台/JitWeb 修复
业务兜底(本拦截器) 已上线验证,可显著降低 duplicate 概率

若平台后续提供官方幂等键或前端防重,本拦截器可逐步下线或仅保留作纵深防御。

说明: 帖中未贴完整源码与 curl,需要时可私信或另开「方案分享」帖贴 interceptor.py 要点(源码无密钥,可直接公开)。