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<String, Object> compile(Map<String, NodeModel> nodes,
|
Function<String, Map<String, Object>> formLoader,
|
Integer flowType) {
|
Map<String, Object> policy = compile(nodes, formLoader);
|
if (!FilePermissionPolicy.parse(policy).isEmpty() && !Arrays.asList(0, 1).contains(flowType)) {
|
throw new IllegalArgumentException("当前仅标准流程和简易流程支持文件权限配置");
|
}
|
return policy;
|
}
|
|
public static Map<String, Object> compile(Map<String, NodeModel> nodes,
|
Function<String, Map<String, Object>> formLoader) {
|
List<Map<String, Object>> rules = new ArrayList<>();
|
String startFormId = nodes.values().stream().filter(n -> "start".equals(n.getType()))
|
.map(NodeModel::getFormId).filter(FilePermissionPolicyCompiler::hasText).findFirst().orElse(null);
|
Set<String> formIds = new HashSet<>();
|
Set<String> permissionNodeIds = new HashSet<>();
|
for (Map.Entry<String, NodeModel> 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<String, Object> field : node.getFormOperates()) {
|
if (!field.containsKey("filePermissions")) continue;
|
Map<FilePermissionPolicy.Action, FilePermissionPolicy.Effect> 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<String, Object> 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<String, String> actionMap = new LinkedHashMap<>();
|
actions.forEach((action, effect) -> actionMap.put(action.name(), effect.name()));
|
rule.put("actions", actionMap);
|
rules.add(rule);
|
}
|
}
|
Map<String, Object> policy = new LinkedHashMap<>();
|
policy.put("version", 1);
|
policy.put("rules", rules);
|
List<FilePermissionPolicy.Rule> parsed = FilePermissionPolicy.parse(policy);
|
// 没有规则时完全保留旧路径,不增加表单远程查询。
|
Map<String, Set<String>> fields = new HashMap<>();
|
for (FilePermissionPolicy.Rule rule : parsed) {
|
if (!formIds.contains(rule.formId)) throw new IllegalArgumentException("文件规则引用了流程外表单");
|
fields.computeIfAbsent(rule.formId, id -> {
|
Map<String, Object> form = formLoader.apply(id);
|
if (form == null) throw new IllegalArgumentException("文件规则引用的发布表单不存在");
|
Set<String> 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<String> 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<String, Object> toMap(FilePermissionPolicy.Rule rule) {
|
Map<String, Object> 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<String, String> 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(); }
|
}
|