package jnpf.dmsService; import jnpf.base.UserInfo; import jnpf.dmsEntity.permission.entity.DmsDocumentEntity; import jnpf.dmsEntity.permission.entity.DmsFileVersionEntity; import jnpf.dmsEntity.permission.entity.DmsPermissionResourceEntity; import jnpf.dmsEntity.permission.enums.DmsPermissionAction; import jnpf.dmsEntity.permission.enums.DmsResourceType; import jnpf.dmsEntity.storage.entity.DmsFileObjectEntity; import jnpf.dmsEntity.storage.form.DmsFileUploadRegisterForm; import jnpf.dmsEntity.storage.vo.DmsFileUploadVO; import jnpf.dmsIntegration.audit.DmsAuditAdapter; import jnpf.dmsMapper.DmsDocumentMapper; import jnpf.dmsMapper.DmsFileObjectMapper; import jnpf.dmsMapper.DmsFileVersionMapper; import jnpf.dmsMapper.DmsPermissionResourceMapper; import jnpf.dmsMapper.DmsVersionStateMapper; import jnpf.dmsPermission.DmsCurrentIdentity; import jnpf.dmsPermission.DmsPermissionError; import jnpf.dmsPermission.DmsPermissionException; import lombok.RequiredArgsConstructor; import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Locale; import java.util.Objects; @Service @RequiredArgsConstructor public class DmsFileUploadService { private static final String SOURCE_PREFIX = "upload:"; private final DmsCurrentIdentity currentIdentity; private final DmsPermissionDecisionService decisionService; private final DmsDocumentMapper documentMapper; private final DmsFileVersionMapper fileVersionMapper; private final DmsPermissionResourceMapper resourceMapper; private final DmsFileObjectMapper fileObjectMapper; private final DmsVersionStateMapper versionStateMapper; private final DmsAuditAdapter auditAdapter; @Transactional public DmsFileUploadVO register(DmsFileUploadRegisterForm form, String serviceId) { UserInfo user = currentIdentity.requireUser(); String tenantId = currentIdentity.tenantId(user); String sourceId = form.getSourceId().trim(); decisionService.requireAllowed(DmsResourceType.FOLDER, form.getFolderId(), DmsPermissionAction.CREATE); DmsFileUploadVO existing = fileObjectMapper.selectUploadBySource(tenantId, serviceId, sourceId); if (existing != null) { validateRetry(existing, form); return existing; } try { insertDocument(form, tenantId, user.getUserId(), serviceId, sourceId); insertFileVersion(form, tenantId, user.getUserId(), serviceId, sourceId); insertDirectEffectiveState(form, tenantId, user.getUserId()); insertFileObject(form, tenantId, user.getUserId(), serviceId, sourceId); } catch (DuplicateKeyException exception) { throw conflict("上传登记的资源 ID、source_id、版本号或对象键已被占用", exception); } auditAdapter.dataChange("CREATE", "上传 DMS 文件", "dms_file_objects", form.getFileObjectId().toString(), null, "{\"documentId\":\"" + form.getDocumentId() + "\",\"fileVersionId\":\"" + form.getFileVersionId() + "\"}"); return fileObjectMapper.selectUploadBySource(tenantId, serviceId, sourceId); } private void insertDocument(DmsFileUploadRegisterForm form, String tenantId, String userId, String serviceId, String sourceId) { DmsDocumentEntity document = new DmsDocumentEntity(); document.setId(form.getDocumentId()); document.setTenantId(tenantId); document.setFolderId(form.getFolderId()); document.setDocumentNo(form.getDocumentNo().trim()); document.setName(form.getDocumentName().trim()); document.setDocumentType(form.getDocumentType().trim()); document.setResponsibleDeptId(form.getResponsibleDeptId().trim()); document.setCreatedBy(userId); documentMapper.insert(document); insertRegistry(tenantId, form.getDocumentId(), DmsResourceType.DOCUMENT, registrySource(sourceId, "document"), serviceId); } private void insertFileVersion(DmsFileUploadRegisterForm form, String tenantId, String userId, String serviceId, String sourceId) { DmsFileVersionEntity version = new DmsFileVersionEntity(); version.setId(form.getFileVersionId()); version.setTenantId(tenantId); version.setDocumentId(form.getDocumentId()); version.setVersionNo(1L); version.setVersionLabel(form.getVersionLabel().trim()); version.setFileName(form.getFileName().trim()); version.setMimeType(trimToNull(form.getMimeType())); version.setSizeBytes(form.getSizeBytes()); version.setChecksumSha256(form.getChecksumSha256().toLowerCase(Locale.ROOT)); version.setPreparedBy(form.getPreparedBy().trim()); version.setPreparedOn(form.getPreparedOn()); version.setCreatedBy(userId); fileVersionMapper.insert(version); insertRegistry(tenantId, form.getFileVersionId(), DmsResourceType.FILE_VERSION, registrySource(sourceId, "version:1"), serviceId); } private void insertFileObject(DmsFileUploadRegisterForm form, String tenantId, String userId, String serviceId, String sourceId) { DmsFileObjectEntity object = new DmsFileObjectEntity(); object.setId(form.getFileObjectId()); object.setTenantId(tenantId); object.setFileVersionId(form.getFileVersionId()); object.setStoragePlatform(form.getStoragePlatform().trim()); object.setBucketName(form.getBucketName().trim()); object.setBasePath(trimToEmpty(form.getBasePath())); object.setObjectPath(form.getObjectPath().trim()); object.setObjectName(form.getObjectName().trim()); object.setSourceServiceId(serviceId); object.setSourceId(sourceId); object.setCreatedBy(userId); fileObjectMapper.insert(object); } private void insertDirectEffectiveState(DmsFileUploadRegisterForm form, String tenantId, String userId) { versionStateMapper.insertDirectEffective(tenantId, form.getDocumentId(), form.getFileVersionId(), userId); } private void insertRegistry(String tenantId, java.util.UUID id, DmsResourceType type, String sourceId, String serviceId) { DmsPermissionResourceEntity registry = new DmsPermissionResourceEntity(); registry.setId(id); registry.setTenantId(tenantId); registry.setResourceType(type.name()); registry.setSourceType("SYSTEM"); registry.setSourceId(sourceId); registry.setRegisteredBy(serviceId); resourceMapper.insert(registry); } private void validateRetry(DmsFileUploadVO existing, DmsFileUploadRegisterForm form) { if (!Objects.equals(existing.getFolderId(), form.getFolderId()) || !Objects.equals(existing.getDocumentNo(), form.getDocumentNo().trim()) || !Objects.equals(existing.getDocumentName(), form.getDocumentName().trim()) || !Objects.equals(existing.getDocumentType(), form.getDocumentType().trim()) || !Objects.equals(existing.getResponsibleDeptId(), form.getResponsibleDeptId().trim()) || !Objects.equals(existing.getVersionLabel(), form.getVersionLabel().trim()) || !Objects.equals(existing.getFileName(), form.getFileName().trim()) || !Objects.equals(existing.getMimeType(), trimToNull(form.getMimeType())) || !Objects.equals(existing.getSizeBytes(), form.getSizeBytes()) || !equalsIgnoreCase(existing.getChecksumSha256(), form.getChecksumSha256()) || !Objects.equals(existing.getPreparedBy(), form.getPreparedBy().trim()) || !Objects.equals(existing.getPreparedOn(), form.getPreparedOn())) { throw DmsPermissionException.of(DmsPermissionError.IDEMPOTENCY_CONFLICT, "source_id 对应的上传内容与本次请求不一致"); } } private String registrySource(String sourceId, String suffix) { return SOURCE_PREFIX + sourceId + ":" + suffix; } private String trimToNull(String value) { return value == null || value.trim().isEmpty() ? null : value.trim(); } private String trimToEmpty(String value) { return value == null ? "" : value.trim(); } private boolean equalsIgnoreCase(String left, String right) { return left == null ? right == null : right != null && left.equalsIgnoreCase(right); } private DmsPermissionException conflict(String message, Throwable cause) { DmsPermissionException exception = DmsPermissionException.of(DmsPermissionError.SOURCE_CONFLICT, message); exception.initCause(cause); return exception; } }