刘光辉
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
package jnpf.dmsService;
 
import jnpf.audit.sdk.annotation.AuditLog;
import jnpf.dmsEntity.storage.approval.DmsDisableOccupancy;
import jnpf.dmsMapper.DmsDisableApplicationMapper;
import jnpf.exception.DataException;
import jnpf.dmsEntity.storage.form.DmsDisableSubmitValidationForm;
import jnpf.flowable.WorkFlowApi;
import jnpf.flowable.entity.TaskEntity;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
 
/** Workflow-event side effect only; native forms still own application persistence. */
@Service
@RequiredArgsConstructor
public class DmsDisableOccupancyService {
    private static final Set<Integer> RELEASED = Set.of(3, 4, 7, 9);
    private final DmsDisableApplicationMapper mapper;
    private final WorkFlowApi workflow;
    @Value("${dms.disable-occupancy.enabled:false}")
    private boolean enabled;
 
    @Transactional(rollbackFor = Exception.class)
    @AuditLog(action = "DMS_SYNC_DISABLE_OCCUPANCY", bizCodeExpr = "#taskId")
    public boolean onEvent(String tenantId, String flowId, String taskId, String operation, String eventType) {
        DmsApprovedDisableService.requireOperation(operation);
        if (!Set.of("start", "end", "back", "reject", "recall").contains(eventType))
            throw new DataException("不支持的文件占用流程事件");
        if (!enabled && !"end".equals(eventType)) throw new DataException("文件占用登记尚未启用,需先完成技术迁移及流程事件配置");
        TaskEntity task = workflow.getInfoSubmit(taskId);
        if (task == null || !taskId.equals(task.getId()) || task.getStatus() == null
                || (!flowId.equals(task.getFlowId()) && !flowId.equals(task.getTemplateId())))
            throw new DataException("文件占用事件与真实工作流任务不匹配");
        DmsDisableOccupancy application = mapper.application(tenantId, taskId, operation);
        if (application == null || (!Objects.equals(application.getFlowId(), task.getFlowId())
                && !Objects.equals(application.getFlowId(), task.getTemplateId()))
                || !Objects.equals(application.getFlowState(), task.getStatus()))
            throw new DataException("文件占用事件与原生申请归属或流程状态不匹配");
        if (RELEASED.contains(task.getStatus()) && task.getEndTime() != null) {
            if (enabled) mapper.releaseTask(tenantId, taskId, operation);
            return false;
        }
        if ("end".equals(eventType)) return task.getStatus() == 2 && task.getEndTime() != null;
        // Returned or recalled but unfinished workflows keep their existing snapshot.
        if (!"start".equals(eventType) || !Set.of(1, 5, 6, 10).contains(task.getStatus())) return false;
        List<DmsDisableOccupancy> rows = mapper.applicationFiles(tenantId, application.getApplicationId(), operation);
        if (rows.isEmpty()) throw new DataException("原生撤销或失效申请没有主文件明细");
        Set<String> files = new HashSet<>();
        for (DmsDisableOccupancy row : rows) {
            if (row.getFileId() == null || row.getFileId().isBlank() || row.getFolderId() == null || row.getFolderId().isBlank() || !files.add(row.getFileId()))
                throw new DataException("申请明细重复或缺少有效主文件/专属目录关联");
        }
        requireAvailable(tenantId, files.stream().toList(), taskId);
        // One local transaction replaces this task's snapshot; no cross-workflow exclusive lock.
        mapper.releaseTask(tenantId, taskId, operation);
        for (DmsDisableOccupancy row : rows) {
            row.setTenantId(tenantId);
            row.setOperation(operation);
            row.setApplicationId(application.getApplicationId());
            row.setFlowId(task.getFlowId());
            row.setTaskId(taskId);
            mapper.occupy(row);
        }
        return false;
    }
    public void validateSubmission(String tenantId, String userId, String operation, DmsDisableSubmitValidationForm form) {
        DmsApprovedDisableService.requireOperation(operation);
        if (!enabled) throw new DataException("文件占用登记尚未启用,不能提交撤销或失效申请");
        List<String> files = form.getFileIds();
        if (files == null || files.isEmpty() || files.stream().anyMatch(id -> id == null || id.isBlank())
                || new HashSet<>(files).size() != files.size())
            throw new DataException("请至少选择一个主文件,同一文件不能重复申请");
        String ownTask = null;
        if (form.getTaskId() != null && !form.getTaskId().isBlank()) {
            TaskEntity task = workflow.getInfoSubmit(form.getTaskId());
            DmsDisableOccupancy application = mapper.application(tenantId, form.getTaskId(), operation);
            if (task == null || !form.getTaskId().equals(task.getId()) || application == null
                    || (!form.getFlowId().equals(task.getFlowId()) && !form.getFlowId().equals(task.getTemplateId()))
                    || (!Objects.equals(application.getFlowId(), task.getFlowId()) && !Objects.equals(application.getFlowId(), task.getTemplateId()))
                    || task.getStatus() == null || !Set.of(0,1,5,6,8,9,10).contains(task.getStatus()) || task.getEndTime() != null)
                throw new DataException("仅可沿用本人未结束的原申请,不能排除其他申请的占用");
            if (Set.of(0,8,9).contains(task.getStatus())) {
                if (userId == null || userId.isBlank() || !Objects.equals(userId, application.getCreatorUserId()))
                    throw new DataException("仅可沿用本人未结束的原申请");
            } else {
                // Native beforeSubmit is also invoked for approval. Only an unchanged
                // persisted file selection may reuse an in-progress task's snapshot.
                Set<String> persisted = new HashSet<>();
                for (DmsDisableOccupancy row : mapper.applicationFiles(tenantId,application.getApplicationId(),operation))
                    persisted.add(row.getFileId());
                if (!persisted.equals(new HashSet<>(files)))
                    throw new DataException("审批中的文件明细与原申请不一致,不能变更占用范围");
            }
            ownTask = task.getId();
        }
        requireAvailable(tenantId, files, ownTask);
    }
 
    private void requireAvailable(String tenantId, List<String> files, String ownTask) {
        if (mapper.selectedFiles(tenantId, files).size() != files.size())
            throw new DataException("所选文件已停用、不是有效主文件或缺少专属目录,请重新选择");
        if (mapper.conflictingFiles(tenantId, files, ownTask) > 0)
            throw new DataException("所选文件已有未结束的撤销或失效申请,不能重复提交");
    }
 
}