此前我发过 [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 放行(避免拦截器故障拖垮全部审批)
验证(已做)
- 并发重放:Network 复制
doHandlecURL,同一 body 并发 POST 两次 → 一次成功、一次被拦 - 已提交后重放:手动提交成功后相同 body 再重放 → 均被拦,列表不新增
- 日志:
[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.request 读 doHandle 的 argDict、caches.Default(Redis)。
与 bug 帖的关系
| 层级 | 状态 |
|---|---|
| 平台根因(双发 + 无幂等) | 仍待平台/JitWeb 修复 |
| 业务兜底(本拦截器) | 已上线验证,可显著降低 duplicate 概率 |
若平台后续提供官方幂等键或前端防重,本拦截器可逐步下线或仅保留作纵深防御。
说明: 帖中未贴完整源码与 curl,需要时可私信或另开「方案分享」帖贴 interceptor.py 要点(源码无密钥,可直接公开)。