刘光辉
3 小时以前 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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package jnpf.flowable.util.filepermission;
 
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import jnpf.flowable.model.filepermission.FilePermissionPolicy;
 
import java.util.*;
 
/** 实例创建时的独立配置快照;不含当前授权,不读取最新模板,也不以引擎实例 ID 定位。 */
public final class FilePermissionSnapshot {
    private static final ObjectMapper JSON = new ObjectMapper()
            .enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION)
            .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);
    private final String json;
    private final String sourceFlowId;
    private final List<FilePermissionPolicy.Rule> rules;
    private final Set<String> enteredNodes;
    private final Set<String> activeReleaseRules;
 
    private FilePermissionSnapshot(String json, String sourceFlowId, List<FilePermissionPolicy.Rule> rules,
                                   Set<String> enteredNodes, Set<String> activeReleaseRules) {
        this.json = json;
        this.sourceFlowId = sourceFlowId;
        this.rules = rules;
        this.enteredNodes = Collections.unmodifiableSet(enteredNodes);
        this.activeReleaseRules = Collections.unmodifiableSet(activeReleaseRules);
    }
 
    public static String create(String tenantId, String taskId, String flowId, Map<String, Object> policy) {
        required(tenantId); required(taskId); required(flowId);
        if (FilePermissionPolicy.parse(policy).isEmpty()) return null;
        Map<String, Object> value = new LinkedHashMap<>();
        value.put("version", 1);
        value.put("tenantId", tenantId);
        value.put("taskId", taskId);
        value.put("sourceFlowId", flowId);
        value.put("policy", policy);
        value.put("enteredNodes", Collections.emptyList());
        value.put("activeReleaseRules", Collections.emptyList());
        return encode(value);
    }
 
    /** 仅 SQL NULL 表示原有行为;空串/损坏配置/上下文不匹配均不得降级。 */
    public static FilePermissionSnapshot read(String json, String tenantId, String taskId) {
        required(tenantId); required(taskId);
        if (json == null) return null;
        Map<String, Object> value = decode(json);
        if (!Integer.valueOf(1).equals(value.get("version"))
                || !tenantId.equals(value.get("tenantId")) || !taskId.equals(value.get("taskId"))) {
            throw new IllegalArgumentException("流程文件权限快照版本或实例归属不匹配");
        }
        Object flowId = value.get("sourceFlowId");
        if (!(flowId instanceof String)) throw new IllegalArgumentException("流程文件权限快照缺少来源版本");
        required((String) flowId);
        if (!value.containsKey("policy") || value.get("policy") == null) {
            throw new IllegalArgumentException("流程文件权限快照缺少规则");
        }
        List<FilePermissionPolicy.Rule> rules = FilePermissionPolicy.parse(value.get("policy"));
        if (rules.isEmpty()) throw new IllegalArgumentException("受控实例文件规则为空");
        Set<String> activeReleaseRules = ruleIds(value.get("activeReleaseRules"), "可解除规则状态");
        Set<String> releaseRuleIds = new HashSet<>();
        for (FilePermissionPolicy.Rule rule : rules) {
            if (rule.trigger == FilePermissionPolicy.Trigger.ENTERED
                    && rule.onReturn == FilePermissionPolicy.OnReturn.RELEASE) releaseRuleIds.add(rule.id);
        }
        if (!releaseRuleIds.containsAll(activeReleaseRules)) {
            throw new IllegalArgumentException("流程文件权限快照包含未知的可解除规则");
        }
        return new FilePermissionSnapshot(json, (String) flowId, rules, enteredNodes(value.get("enteredNodes")), activeReleaseRules);
    }
 
    /** 撤销申请派生任务沿用来源实例配置,绑定自己的任务 ID,不读取后来修改的模板。 */
    public String forDerivedTask(String newTaskId) {
        required(newTaskId);
        Map<String, Object> value = decode(json);
        value.put("taskId", newTaskId);
        value.put("enteredNodes", Collections.emptyList());
        value.put("activeReleaseRules", Collections.emptyList());
        return encode(value);
    }
 
    /** 节点进入事实只增不减;规则策略仍保持创建实例时的冻结值。 */
    public String withEnteredNodes(Collection<String> nodeIds) {
        if (nodeIds == null || nodeIds.isEmpty()) return json;
        LinkedHashSet<String> merged = new LinkedHashSet<>(enteredNodes);
        LinkedHashSet<String> active = new LinkedHashSet<>(activeReleaseRules);
        for (String nodeId : nodeIds) {
            required(nodeId);
            merged.add(nodeId);
        }
        for (FilePermissionPolicy.Rule rule : rules) {
            if (rule.trigger == FilePermissionPolicy.Trigger.ENTERED
                    && rule.onReturn == FilePermissionPolicy.OnReturn.RELEASE
                    && nodeIds.contains(rule.triggerNodeId)) active.add(rule.id);
        }
        if (merged.equals(enteredNodes) && active.equals(activeReleaseRules)) return json;
        Map<String, Object> value = decode(json);
        value.put("enteredNodes", new ArrayList<>(merged));
        value.put("activeReleaseRules", new ArrayList<>(active));
        return encode(value);
    }
 
    /** 退回时,引擎判定为目标节点之后并作废的触发节点,其可解除规则退出当前执行轮次。 */
    public String withReturnedPastNodes(Collection<String> invalidatedNodeIds) {
        if (invalidatedNodeIds == null || invalidatedNodeIds.isEmpty() || activeReleaseRules.isEmpty()) return json;
        Set<String> invalidated = new HashSet<>();
        for (String nodeId : invalidatedNodeIds) {
            required(nodeId);
            invalidated.add(nodeId);
        }
        LinkedHashSet<String> active = new LinkedHashSet<>(activeReleaseRules);
        for (FilePermissionPolicy.Rule rule : rules) {
            if (rule.trigger == FilePermissionPolicy.Trigger.ENTERED
                    && rule.onReturn == FilePermissionPolicy.OnReturn.RELEASE
                    && invalidated.contains(rule.triggerNodeId)) active.remove(rule.id);
        }
        if (active.equals(activeReleaseRules)) return json;
        Map<String, Object> value = decode(json);
        value.put("activeReleaseRules", new ArrayList<>(active));
        return encode(value);
    }
 
    public String getSourceFlowId() { return sourceFlowId; }
    public List<FilePermissionPolicy.Rule> getRules() { return rules; }
    public Set<String> getEnteredNodes() { return enteredNodes; }
    public Set<String> getActiveReleaseRules() { return activeReleaseRules; }
 
    private static Set<String> enteredNodes(Object value) {
        if (value == null) return new LinkedHashSet<>(); // 兼容功能启用前已冻结的实例快照。
        return ruleIds(value, "节点历史");
    }
 
    private static Set<String> ruleIds(Object value, String label) {
        if (value == null) return new LinkedHashSet<>();
        if (!(value instanceof Collection<?>)) throw new IllegalArgumentException("流程文件权限快照的" + label + "无效");
        LinkedHashSet<String> result = new LinkedHashSet<>();
        for (Object item : (Collection<?>) value) {
            if (!(item instanceof String)) throw new IllegalArgumentException("流程文件权限快照的" + label + "无效");
            required((String) item);
            result.add((String) item);
        }
        return result;
    }
 
    private static Map<String, Object> decode(String json) {
        try {
            Map<String, Object> value = JSON.readValue(json, new TypeReference<Map<String, Object>>() { });
            if (value == null) throw new IllegalArgumentException("流程文件权限快照无效");
            return value;
        } catch (java.io.IOException e) { throw new IllegalArgumentException("流程文件权限快照损坏", e); }
    }
 
    private static String encode(Map<String, Object> value) {
        try { return JSON.writeValueAsString(value); }
        catch (java.io.IOException e) { throw new IllegalArgumentException("无法生成流程文件权限快照", e); }
    }
 
    private static void required(String value) {
        if (value == null || value.trim().isEmpty()) throw new IllegalArgumentException("流程文件权限缺少实例上下文");
    }
}