刘光辉
118 分钟以前 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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package jnpf.dmsService;
 
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jnpf.dmsEntity.DmsWenjianBanbenEntity;
import jnpf.dmsEntity.DmsWenjianXinzengShenheEntity;
import jnpf.dmsEntity.DmsWenjianXiudingShenheEntity;
import jnpf.dmsEntity.DmsWenjianXiudingShenqingZixiangEntity;
import jnpf.dmsEntity.storage.form.DmsWorkflowFilePrepareForm;
import jnpf.dmsEntity.storage.DmsWorkflowSourceKey;
import jnpf.dmsEntity.storage.enums.DmsWorkflowFileType;
import jnpf.dmsEntity.storage.vo.DmsFileUploadVO;
import jnpf.dmsMapper.DmsFileObjectMapper;
import jnpf.dmsMapper.DmsWenjianXinzengShenheMapper;
import jnpf.dmsMapper.DmsWenjianXiudingShenheMapper;
import jnpf.dmsMapper.DmsWenjianBanbenMapper;
import jnpf.dmsMapper.DmsWenjianXiudingShenqingZixiangMapper;
import jnpf.exception.DataException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
 
import java.net.URI;
import java.util.UUID;
 
@Component
@RequiredArgsConstructor
public class DmsWorkflowApprovalValidator {
    private final ObjectMapper objectMapper;
    private final DmsWenjianBanbenMapper businessVersionMapper;
    private final DmsWenjianXiudingShenqingZixiangMapper revisionDetailMapper;
    private final DmsFileObjectMapper fileObjectMapper;
    private final DmsWenjianXinzengShenheMapper newAuditMapper;
    private final DmsWenjianXiudingShenheMapper revisionAuditMapper;
 
    public String validateNew(DmsWenjianXinzengShenheEntity audit,
                              DmsWorkflowFilePrepareForm form) {
        requireEqual(audit.getWenjianBianhao(), form.getDocumentNo(), "文件编号");
        requireEqual(audit.getWenjianMingcheng(), form.getDocumentName(), "文件名称");
        requireEqual(audit.getWenjianMuluId(), form.getFolderId() == null ? null : form.getFolderId().toString(), "文件目录");
        requireEqual(audit.getGuishuBumenId(), form.getResponsibleDeptId(), "归属部门");
        return approvedFile(audit.getWenjian(), form.getUrl());
    }
 
    public String validateRevision(String tenantId, DmsWenjianXiudingShenheEntity audit,
                                   DmsWorkflowFilePrepareForm form) {
        requireEqual(audit.getWenjianMingcheng(), form.getDocumentName(), "文件名称");
        requireEqual(audit.getWenjianMuluId(), form.getFolderId() == null ? null : form.getFolderId().toString(), "文件目录");
        requireEqual(audit.getGuishuBumenId(), form.getResponsibleDeptId(), "归属部门");
        String businessVersionId = audit.getYuanWenjianBanbenId();
        String businessFileId = audit.getWenjianId();
        if (audit.getXiudingShenqingZixiangId() != null) {
            DmsWenjianXiudingShenqingZixiangEntity detail = revisionDetailMapper.selectByIdForUpdate(
                    tenantId, audit.getXiudingShenqingZixiangId());
            if (detail == null) {
                throw new DataException("修订审核单关联的申请子项不存在");
            }
            businessVersionId = detail.getYuanWenjianBanbenId();
            businessFileId = detail.getWenjianId();
        }
        if (businessVersionId == null || businessFileId == null) {
            throw new DataException("修订审核单缺少批准的原文件版本");
        }
        DmsWenjianBanbenEntity version = businessVersionMapper.selectById(tenantId, businessVersionId);
        if (version == null || !businessFileId.equals(version.getWenjianId())) {
            throw new DataException("修订审核单关联的业务文件版本不存在或不匹配");
        }
        // 业务版本ID与文件中心UUID不是同一套ID,必须通过已有映射核对。
        try {
            if (form.getVersionId() == null || !form.getVersionId().equals(fileCenterVersion(tenantId, version))) {
                throw new DataException("versionId与审核批准的文件中心版本不一致");
            }
        } catch (IllegalArgumentException | NullPointerException exception) {
            throw new DataException("审核批准的原版本尚未关联有效的文件中心版本");
        }
        return approvedFile(audit.getWenjian(), form.getUrl());
    }
 
    private UUID fileCenterVersion(String tenantId, DmsWenjianBanbenEntity version) {
        if (version.getWenjianZhongxinBanbenId() != null) {
            return UUID.fromString(version.getWenjianZhongxinBanbenId());
        }
        // 兼容尚未回写映射的业务版本,只从其来源审核单的任务登记解析,不能信任请求UUID。
        if (version.getLaiyuanDanjuId() == null || version.getLaiyuanZixiangId() != null) {
            throw new DataException("审核批准的原版本尚未关联文件中心版本");
        }
        String key = null;
        if ("xinzeng_shenhe".equals(version.getLaiyuanDanjuLeixing())) {
            DmsWenjianXinzengShenheEntity source = newAuditMapper.selectOne(
                    new LambdaQueryWrapper<DmsWenjianXinzengShenheEntity>()
                            .eq(DmsWenjianXinzengShenheEntity::getTenantId, tenantId)
                            .eq(DmsWenjianXinzengShenheEntity::getId, version.getLaiyuanDanjuId())
                            .isNull(DmsWenjianXinzengShenheEntity::getDeleteMark));
            if (source != null) {
                key = DmsWorkflowSourceKey.of(source.getFlowId(), source.getFlowTaskId(), DmsWorkflowFileType.CREATE);
            }
        } else if ("xiuding_shenhe".equals(version.getLaiyuanDanjuLeixing())) {
            DmsWenjianXiudingShenheEntity source = revisionAuditMapper.selectOne(
                    new LambdaQueryWrapper<DmsWenjianXiudingShenheEntity>()
                            .eq(DmsWenjianXiudingShenheEntity::getTenantId, tenantId)
                            .eq(DmsWenjianXiudingShenheEntity::getId, version.getLaiyuanDanjuId())
                            .isNull(DmsWenjianXiudingShenheEntity::getDeleteMark));
            if (source != null) {
                key = DmsWorkflowSourceKey.of(source.getFlowId(), source.getFlowTaskId(), DmsWorkflowFileType.REVISE);
            }
        }
        DmsFileUploadVO registered = key == null ? null
                : fileObjectMapper.selectUploadBySource(tenantId, "jnpf-file", key);
        if (registered == null || registered.getFileVersionId() == null) {
            throw new DataException("审核批准的原版本尚未登记到文件中心");
        }
        return registered.getFileVersionId();
    }
 
    private void requireEqual(String approved, String requested, String label) {
        if (approved == null || approved.trim().isEmpty() || requested == null
                || !approved.trim().equals(requested.trim())) {
            throw new DataException(label + "与审核批准的内容不一致");
        }
    }
 
    String approvedFile(String json, String url) {
        try {
            JsonNode root = objectMapper.readTree(json);
            JsonNode file = findFile(root, filePath(url));
            if (file == null) {
                throw new DataException("文件URL不在审核批准的附件中");
            }
            return objectMapper.writeValueAsString(file);
        } catch (DataException exception) {
            throw exception;
        } catch (Exception exception) {
            throw new DataException("审核单附件信息无效,无法验证批准的文件");
        }
    }
 
    private JsonNode findFile(JsonNode node, String path) {
        if (node == null) {
            return null;
        }
        if (node.isObject() && node.path("url").isTextual()) {
            if (path.equals(filePath(node.get("url").asText()))) {
                return node;
            }
        }
        if (node.isContainerNode()) {
            for (JsonNode child : node) {
                JsonNode found = findFile(child, path);
                if (found != null) {
                    return found;
                }
            }
        }
        return null;
    }
 
    private String filePath(String url) {
        URI uri = URI.create(url.trim());
        if (uri.getScheme() != null && !"http".equalsIgnoreCase(uri.getScheme())
                && !"https".equalsIgnoreCase(uri.getScheme())) {
            throw new DataException("审核附件不是公共文件路径");
        }
        String path = uri.getPath();
        String prefix = "/api/file/Image/";
        int start = path == null ? -1 : path.indexOf(prefix);
        if (start < 0) {
            throw new DataException("审核附件不是公共文件路径");
        }
        String suffix = path.substring(start + prefix.length());
        String[] parts = suffix.split("/", -1);
        if (parts.length != 2) {
            throw new DataException("审核附件路径格式不正确");
        }
        for (String part : parts) {
            if (part.isBlank() || part.contains("\\") || ".".equals(part) || "..".equals(part)) {
                throw new DataException("审核附件路径格式不正确");
            }
        }
        return suffix;
    }
}