刘光辉
4 小时以前 06f314545ac3514d7ce28939920866ae8188d31c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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(); }
}