package jnpf.flowable.util.filepermission; import jnpf.flowable.model.filepermission.FilePermissionPolicy; import jnpf.flowable.model.templatenode.nodejson.NodeModel; import java.util.*; import java.util.function.Function; /** 将节点字段动作与跨人规则编译为同一契约。表单加载器只能读取服务端配置。 */ public final class FilePermissionPolicyCompiler { private FilePermissionPolicyCompiler() { } /** 发布和新实例使用服务端流程类型;无规则配置不限制原有流程。 */ public static Map compile(Map nodes, Function> formLoader, Integer flowType) { Map policy = compile(nodes, formLoader); if (!FilePermissionPolicy.parse(policy).isEmpty() && !Arrays.asList(0, 1).contains(flowType)) { throw new IllegalArgumentException("当前仅标准流程和简易流程支持文件权限配置"); } return policy; } public static Map compile(Map nodes, Function> formLoader) { List> rules = new ArrayList<>(); String startFormId = nodes.values().stream().filter(n -> "start".equals(n.getType())) .map(NodeModel::getFormId).filter(FilePermissionPolicyCompiler::hasText).findFirst().orElse(null); Set formIds = new HashSet<>(); Set permissionNodeIds = new HashSet<>(); for (Map.Entry entry : nodes.entrySet()) { NodeModel node = entry.getValue(); String formId = hasText(node.getFormId()) ? node.getFormId() : startFormId; if (Arrays.asList("start", "approver", "processing").contains(node.getType())) { permissionNodeIds.add(entry.getKey()); if (hasText(formId)) formIds.add(formId); } for (FilePermissionPolicy.Rule rule : FilePermissionPolicy.parse(node.getFilePermissionPolicy())) { if (rule.id.startsWith("@field/")) throw new IllegalArgumentException("文件规则 ID 使用了保留前缀"); rules.add(toMap(rule)); } if (node.getFormOperates() == null) continue; for (Map field : node.getFormOperates()) { if (!field.containsKey("filePermissions")) continue; Map actions = FilePermissionPolicy.parseActions(field.get("filePermissions")); if (actions.isEmpty()) throw new IllegalArgumentException("文件动作为空"); if (!Arrays.asList("start", "approver", "processing").contains(node.getType())) { throw new IllegalArgumentException("该节点不支持当前办理人文件权限"); } if (!(field.get("id") instanceof String) || !hasText((String) field.get("id"))) { throw new IllegalArgumentException("文件权限缺少字段标识"); } Map rule = new LinkedHashMap<>(); rule.put("id", "@field/" + entry.getKey() + "/" + field.get("id")); rule.put("formId", formId); rule.put("field", field.get("id")); rule.put("subject", "start".equals(node.getType()) ? "CREATOR" : "CURRENT_HANDLERS"); rule.put("trigger", "ACTIVE"); rule.put("triggerNodeId", entry.getKey()); Map actionMap = new LinkedHashMap<>(); actions.forEach((action, effect) -> actionMap.put(action.name(), effect.name())); rule.put("actions", actionMap); rules.add(rule); } } Map policy = new LinkedHashMap<>(); policy.put("version", 1); policy.put("rules", rules); List parsed = FilePermissionPolicy.parse(policy); // 没有规则时完全保留旧路径,不增加表单远程查询。 Map> fields = new HashMap<>(); for (FilePermissionPolicy.Rule rule : parsed) { if (!formIds.contains(rule.formId)) throw new IllegalArgumentException("文件规则引用了流程外表单"); fields.computeIfAbsent(rule.formId, id -> { Map form = formLoader.apply(id); if (form == null) throw new IllegalArgumentException("文件规则引用的发布表单不存在"); Set result = new HashSet<>(); collectFields(form.get("fields"), null, result); return result; }); } FilePermissionPolicy.validateReferences(parsed, fields, permissionNodeIds); return policy; } private static void collectFields(Object value, String parent, Set result) { if (!(value instanceof List)) throw new IllegalArgumentException("发布表单字段结构无效"); for (Object item : (List) value) { if (!(item instanceof Map)) throw new IllegalArgumentException("发布表单字段结构无效"); Map field = (Map) item; Object configValue = field.get("__config__"); if (!(configValue instanceof Map)) throw new IllegalArgumentException("发布表单字段配置无效"); Map config = (Map) configValue; Object id = field.get("__vModel__"); if ("uploadFile".equals(config.get("jnpfKey")) && id instanceof String && hasText((String) id)) { if (!result.add(parent == null ? (String) id : parent + "-" + id)) { throw new IllegalArgumentException("发布表单附件字段标识重复"); } } if (config.get("children") instanceof List) { String nextParent = parent; if ("table".equals(config.get("jnpfKey"))) { if (!(id instanceof String) || !hasText((String) id) || parent != null) { throw new IllegalArgumentException("子表附件字段结构无效"); } nextParent = (String) id; } collectFields(config.get("children"), nextParent, result); } } } private static Map toMap(FilePermissionPolicy.Rule rule) { Map result = new LinkedHashMap<>(); result.put("id", rule.id); result.put("formId", rule.formId); result.put("field", rule.field); result.put("subject", rule.subject.name()); result.put("trigger", rule.trigger.name()); if (rule.subjectNodeId != null) result.put("subjectNodeId", rule.subjectNodeId); if (rule.triggerNodeId != null) result.put("triggerNodeId", rule.triggerNodeId); if (rule.onReturn != null) result.put("onReturn", rule.onReturn.name()); Map actions = new LinkedHashMap<>(); rule.actions.forEach((action, effect) -> actions.put(action.name(), effect.name())); result.put("actions", actions); return result; } private static boolean hasText(String value) { return value != null && !value.trim().isEmpty(); } }