package cn.iocoder.yudao.module.bpm.framework.flowable.core.util; import cn.hutool.core.util.StrUtil; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; /** * Flowable 流程定义编号工具 * *
流程定义编号形如 {@code qingjia:2:553a7ac9-...},包含冒号。 * 前端如果对该值做了 URL 编码后再作为查询参数传递(双重编码), * Spring 只解码一层,最终拿到的仍是 {@code qingjia%3A2%3A...}, * Flowable 按该编号查不到流程定义,直接抛 FlowableObjectNotFoundException 导致 500。 * *
这里在入口处做一次幂等的归一化:只在确实残留 % 转义时才解码, * 合法的流程定义编号不可能包含 %,因此不会误伤。 * * @author 超级管理员 */ public class BpmProcessDefinitionIdUtils { /** 最多解码层数,防止异常输入无限循环 */ private static final int MAX_DECODE_TIMES = 5; private BpmProcessDefinitionIdUtils() { } /** * 归一化流程定义编号:最多解码一层残留的 URL 转义 * * @param id 流程定义编号 * @return 归一化后的编号;入参为空时原样返回 */ public static String normalize(String id) { if (StrUtil.isBlank(id) || id.indexOf('%') < 0) { return id; } // 前端可能编码多次(1~3 层都见过),逐层解码直到不再含 % 转义 String current = id; for (int i = 0; i < MAX_DECODE_TIMES && current.indexOf('%') >= 0; i++) { String decoded; try { decoded = URLDecoder.decode(current, StandardCharsets.UTF_8); } catch (Exception ignored) { // 解码失败说明不是 URL 转义,保持原值 break; } if (decoded.equals(current)) { break; } current = decoded; } return current; } }