刘光辉
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
package jnpf.flowable.service;
 
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jnpf.base.entity.VisualdevEntity;
import jnpf.exception.WorkFlowException;
import jnpf.flowable.entity.TaskEntity;
import jnpf.flowable.mapper.FilePermissionSnapshotMapper;
import jnpf.flowable.model.filepermission.FilePermissionTaskState;
import jnpf.flowable.model.filepermission.FilePermissionPolicy;
import jnpf.flowable.enums.NodeEnum;
import jnpf.flowable.model.templatenode.nodejson.NodeModel;
import jnpf.flowable.util.ServiceUtil;
import jnpf.flowable.util.filepermission.FilePermissionPolicyCompiler;
import jnpf.flowable.util.filepermission.FilePermissionSnapshot;
import jnpf.util.UserProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
 
import java.util.*;
 
/** 只扩展原生任务 INSERT 的快照内容,绝不在已有任务读写时补启用新规则。 */
@Service
@RequiredArgsConstructor
public class FilePermissionSnapshotService {
    /** 仅成功查库且完全无任务行时产生;查询失败或已删除任务不能冒充菜单草稿。 */
    public static final class TaskNotFoundException extends IllegalArgumentException { }
    private static final ObjectMapper JSON = new ObjectMapper();
    private final FilePermissionSnapshotMapper mapper;
    private final ServiceUtil serviceUtil;
 
    /** 只能由原生“新任务”分支调用;返回值与任务在同一 INSERT 中持久化。 */
    public void prepareNewTask(TaskEntity task, String sourceFlowId, Map<String, NodeModel> nodes) throws WorkFlowException {
        try {
            Map<String, Object> policy = FilePermissionPolicyCompiler.compile(nodes, this::loadForm, task.getFlowType());
            if (FilePermissionPolicy.parse(policy).isEmpty()) {
                task.setFilePermissionSnapshot(null);
                return;
            }
            String created = FilePermissionSnapshot.create(tenantId(), task.getId(), sourceFlowId, policy);
            List<String> starts = new ArrayList<>();
            for (Map.Entry<String, NodeModel> entry : nodes.entrySet()) {
                NodeModel node = entry.getValue();
                if (node != null && NodeEnum.START.getType().equals(node.getType())) {
                    starts.add(node.getNodeId() == null || node.getNodeId().trim().isEmpty() ? entry.getKey() : node.getNodeId());
                }
            }
            task.setFilePermissionSnapshot(FilePermissionSnapshot.read(created, tenantId(), task.getId()).withEnteredNodes(starts));
        } catch (RuntimeException e) {
            // 原生提交在 WorkFlowException 分支补偿已启动的引擎实例;读取配置失败同样进入该分支。
            throw configurationError(e);
        }
    }
 
    /** 在原生发布入口、引擎部署之前校验;不写表单或接管流程发布。 */
    public void validateConfiguration(Map<String, NodeModel> nodes, Integer flowType) throws WorkFlowException {
        try { FilePermissionPolicyCompiler.compile(nodes, this::loadForm, flowType); }
        catch (RuntimeException e) { throw configurationError(e); }
    }
 
    /** 内部读取不代表用户有访问任务的权限;调用方仍须原生准入与附件归属检查。 */
    public FilePermissionSnapshot find(String taskId) {
        if (taskId == null || taskId.trim().isEmpty()) throw new IllegalArgumentException("缺少流程任务标识");
        FilePermissionTaskState state = mapper.find(taskId);
        if (state == null) throw new TaskNotFoundException();
        if (!taskId.equals(state.getTaskId()) || state.getDeleteMark() != null) throw new IllegalArgumentException("流程任务不可用");
        return FilePermissionSnapshot.read(state.getSnapshot(), tenantId(), taskId);
    }
 
    public void prepareDerivedTask(TaskEntity task, String sourceTaskId) throws WorkFlowException {
        try {
            FilePermissionSnapshot source = find(sourceTaskId);
            task.setFilePermissionSnapshot(source == null ? null : source.forDerivedTask(task.getId()));
        } catch (IllegalArgumentException e) {
            throw new WorkFlowException("无法读取来源流程的文件权限:" + e.getMessage());
        }
    }
 
    /** 在原生节点激活路径内单调追加;CAS 防止并行激活互相覆盖。 */
    public void markEntered(String taskId, Collection<String> nodeIds) throws WorkFlowException {
        if (nodeIds == null || nodeIds.isEmpty()) return;
        update(taskId, snapshot -> snapshot.withEnteredNodes(nodeIds), "无法记录流程文件权限节点状态:");
    }
 
    /** 与原生退回使用同一后续节点集合;只有本轮被作废的触发节点才解除持续规则。 */
    public void markReturnedPast(String taskId, Collection<String> invalidatedNodeIds) throws WorkFlowException {
        if (invalidatedNodeIds == null || invalidatedNodeIds.isEmpty()) return;
        update(taskId, snapshot -> snapshot.withReturnedPastNodes(invalidatedNodeIds), "无法更新退回后的文件权限:");
    }
 
    private void update(String taskId, java.util.function.Function<FilePermissionSnapshot, String> change,
                        String errorPrefix) throws WorkFlowException {
        try {
            for (int attempt = 0; attempt < 3; attempt++) {
                FilePermissionTaskState state = mapper.find(taskId);
                if (state == null || state.getDeleteMark() != null) throw new IllegalArgumentException("流程任务不可用");
                if (state.getSnapshot() == null) return;
                FilePermissionSnapshot snapshot = FilePermissionSnapshot.read(state.getSnapshot(), tenantId(), taskId);
                String updated = change.apply(snapshot);
                if (updated.equals(state.getSnapshot()) || mapper.compareAndSetSnapshot(taskId, state.getSnapshot(), updated) == 1) return;
            }
            throw new IllegalArgumentException("流程节点状态已变化,请重试");
        } catch (RuntimeException e) {
            throw new WorkFlowException(errorPrefix + e.getMessage());
        }
    }
 
    private Map<String, Object> loadForm(String id) {
        VisualdevEntity form = serviceUtil.getReleaseInfo(id);
        if (form == null || form.getFormData() == null) throw new IllegalArgumentException("发布表单不存在");
        try { return JSON.readValue(form.getFormData(), new TypeReference<Map<String, Object>>() { }); }
        catch (java.io.IOException e) { throw new IllegalArgumentException("发布表单配置无效", e); }
    }
 
    private String tenantId() {
        String tenant = UserProvider.getUser().getTenantId();
        return tenant == null || tenant.trim().isEmpty() ? "0" : tenant;
    }
 
    private WorkFlowException configurationError(RuntimeException e) {
        return new WorkFlowException(e instanceof IllegalArgumentException
                ? "文件权限配置无效:" + e.getMessage() : "暂时无法读取流程文件权限配置,请稍后重试");
    }
}