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("流程文件权限缺少实例上下文");
|
}
|
}
|