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() .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() .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; } }