package jnpf.dmsService; import jnpf.base.UserInfo; import jnpf.dmsEntity.permission.entity.DmsDocumentEntity; import jnpf.dmsEntity.permission.entity.DmsFileVersionEntity; import jnpf.dmsEntity.permission.enums.DmsPermissionAction; import jnpf.dmsEntity.permission.enums.DmsResourceType; import jnpf.dmsEntity.preview.model.DmsFilePreviewTicket; import jnpf.dmsEntity.print.form.DmsFilePrintTicketRequest; import jnpf.dmsEntity.print.form.DmsPrintOperationCreateForm; import jnpf.dmsEntity.print.form.DmsPrintTargetForm; import jnpf.dmsEntity.print.vo.DmsFilePrintTicketVO; import jnpf.dmsEntity.storage.entity.DmsFileObjectEntity; import jnpf.dmsMapper.DmsDocumentMapper; import jnpf.dmsMapper.DmsFileObjectMapper; import jnpf.dmsMapper.DmsFileVersionMapper; import jnpf.dmsPermission.DmsCurrentIdentity; import jnpf.dmsPermission.DmsFileControlPolicy; import jnpf.dmsPermission.DmsPermissionError; import jnpf.dmsPermission.DmsPermissionException; import jnpf.dmsPermission.DmsPermissionExecutionContext; import jnpf.dmsPermission.DmsPermissionGuard; import jnpf.dmsPermission.DmsPermissionOperation; import jnpf.dmsService.pdf.DmsPersistentPdfSources; import jnpf.dmsService.pdf.DmsPdfGenerationTaskService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Collections; import java.util.Date; import java.util.UUID; @Service @RequiredArgsConstructor public class DmsFilePrintService { static final long TICKET_TTL_SECONDS = 5L * 60L; private final DmsCurrentIdentity currentIdentity; private final DmsPermissionGuard permissionGuard; private final DmsFileVersionMapper fileVersionMapper; private final DmsFileObjectMapper fileObjectMapper; private final DmsDocumentMapper documentMapper; private final DmsFilePreviewTicketIssuer ticketIssuer; private final DmsPrintOperationLogService printOperationLogService; private final DmsWatermarkPreviewResolver watermarkPreviewResolver; private final DmsPdfGenerationTaskService pdfGenerationTaskService; @Transactional(rollbackFor = Exception.class) public DmsFilePrintTicketVO createTicket(DmsFilePrintTicketRequest request) { request.normalized(); UserInfo user = currentIdentity.requireUser(); String tenantId = currentIdentity.tenantId(user); DmsFileVersionEntity resolvedVersion = resolveFileVersion(request, tenantId); UUID fileVersionId = resolvedVersion.getId(); long expiresAtMillis = System.currentTimeMillis() + TICKET_TTL_SECONDS * 1000L; PrintSource source = DmsFileControlPolicy.requiresPermissionCheck( resolvedVersion.getShifouShoukong()) ? loadPrintSourceWithPermission(request, tenantId, fileVersionId) : loadPrintSource(tenantId, fileVersionId); DmsFilePreviewTicket payload = payload(user, tenantId, source, expiresAtMillis, request); if (Boolean.TRUE.equals(request.getRequireSerialWatermark())) { if (request.getCustomBarcode() == null || request.getCustomBarcode().size() != 1 || payload.getWatermarks() == null || payload.getWatermarks().stream().noneMatch(mark -> Boolean.TRUE.equals(mark.getCustomBarcode()) && java.util.Objects.equals(mark.getContent(), request.getCustomBarcode().getFirst().getBarValue()) && java.util.Objects.equals(mark.getBarcodeText(), request.getCustomBarcode().getFirst().getBarText()) && mark.getPositions() != null && !mark.getPositions().isEmpty())) { throw new jnpf.exception.DataException("逐份流水水印未启用或未配置位置,已阻止发送无流水文件"); } } String accessTicket = ticketIssuer.issue(payload, TICKET_TTL_SECONDS); recordPrint(request, source); DmsFilePrintTicketVO result = new DmsFilePrintTicketVO(); result.setFileVersionId(fileVersionId); result.setFileName(source.version.getFileName()); result.setMimeType(source.version.getMimeType()); result.setSizeBytes(source.version.getSizeBytes()); result.setCopies(request.getCopies()); result.setPrintPages(trimToNull(request.getPrintPages())); result.setAccessTicket(accessTicket); result.setExpiresAt(new Date(expiresAtMillis)); return result; } private DmsFileVersionEntity resolveFileVersion(DmsFilePrintTicketRequest request, String tenantId) { if (request.getResourceType() == DmsResourceType.DOCUMENT) { DmsFileVersionEntity current = fileVersionMapper.selectCurrentEffective( tenantId, request.getResourceId()); if (current == null) { throw DmsPermissionException.of(DmsPermissionError.RESOURCE_NOT_FOUND, "文件没有现行有效版本"); } return current; } if (request.getResourceType() == DmsResourceType.FILE_VERSION) { DmsFileVersionEntity version = fileVersionMapper.selectReadableByTenantAndId( tenantId, request.getResourceId()); if (version == null) { throw DmsPermissionException.of(DmsPermissionError.RESOURCE_NOT_FOUND, "文件版本不存在"); } return version; } throw DmsPermissionException.of(DmsPermissionError.INVALID_REQUEST, "仅支持打印 DOCUMENT 或 FILE_VERSION"); } private PrintSource loadPrintSourceWithPermission(DmsFilePrintTicketRequest request, String tenantId, UUID fileVersionId) { DmsPermissionExecutionContext context = new DmsPermissionExecutionContext(); context.setResourceType(DmsResourceType.FILE_VERSION); context.setResourceId(fileVersionId); context.setActionCode(DmsPermissionAction.PRINT); context.setOperationId(request.getOperationId()); return permissionGuard.execute(context, new DmsPermissionOperation() { @Override public PrintSource execute() { return loadPrintSource(tenantId, fileVersionId); } @Override public PrintSource replay() { return loadPrintSource(tenantId, fileVersionId); } }); } private PrintSource loadPrintSource(String tenantId, UUID fileVersionId) { DmsFileVersionEntity version = fileVersionMapper.selectReadableByTenantAndId( tenantId, fileVersionId); DmsFileObjectEntity object = fileObjectMapper.selectByTenantAndFileVersionId( tenantId, fileVersionId); DmsDocumentEntity document = version == null ? null : documentMapper.selectByTenantAndId(tenantId, version.getDocumentId()); if (version == null || object == null || document == null) { throw DmsPermissionException.of(DmsPermissionError.RESOURCE_NOT_FOUND, "文件版本、文档或存储对象不存在"); } return new PrintSource(version, object, document); } private DmsFilePreviewTicket payload(UserInfo user, String tenantId, PrintSource source, long expiresAtMillis, DmsFilePrintTicketRequest request) { DmsFilePreviewTicket payload = new DmsFilePreviewTicket(); payload.setScope(DmsFilePreviewTicket.PRINT_SCOPE); payload.setTenantId(tenantId); payload.setUserId(user.getUserId().trim()); payload.setFileVersionId(source.version.getId()); payload.setHistoricalAccess(source.version.getLifecycleStatus() != null && !"EFFECTIVE".equals(source.version.getLifecycleStatus())); payload.setFileName(source.version.getFileName()); payload.setMimeType(source.version.getMimeType()); payload.setSizeBytes(source.version.getSizeBytes()); payload.setStoragePlatform(source.object.getStoragePlatform()); payload.setBucketName(source.object.getBucketName()); payload.setBasePath(source.object.getBasePath()); payload.setObjectPath(source.object.getObjectPath()); payload.setObjectName(source.object.getObjectName()); payload.setPdfSource(DmsPersistentPdfSources.valid(source.version, source.object)); if (payload.getPdfSource() == null) { pdfGenerationTaskService.enqueueMissingEligible(tenantId, source.version.getId(), source.version.getFileName(), source.version.getChecksumSha256()); } payload.setPrintPages(trimToNull(request.getPrintPages())); payload.setWatermarks(watermarkPreviewResolver.resolvePrint( user, tenantId, source.version, request)); payload.setExpiresAt(expiresAtMillis); return payload; } private void recordPrint(DmsFilePrintTicketRequest request, PrintSource source) { String pages = trimToNull(request.getPrintPages()); String remark = pages == null ? null : "打印页码:" + pages; String printName = trimToNull(source.document.getName()); if (printName == null) { printName = source.version.getFileName(); } DmsPrintTargetForm target = new DmsPrintTargetForm(); target.setTargetType("DOCUMENT"); target.setTargetId(source.document.getId().toString()); target.setDocumentId(source.document.getId().toString()); target.setDocumentCode(source.document.getDocumentNo()); target.setDocumentName(printName); target.setVersionNo(versionText(source.version)); target.setRemark(remark); DmsPrintOperationCreateForm form = new DmsPrintOperationCreateForm(); form.setClientEventId(request.getOperationId().toString()); form.setPrintType("CONTROLLED_DOCUMENT"); form.setSourceType("FILE_PREVIEW"); form.setCopies(request.getCopies()); form.setRemark(remark); form.setPrintName(printName); form.setTargets(Collections.singletonList(target)); printOperationLogService.create(form); } private String versionText(DmsFileVersionEntity version) { String label = trimToNull(version.getVersionLabel()); return label == null && version.getVersionNo() != null ? String.valueOf(version.getVersionNo()) : label; } private String trimToNull(String value) { return value == null || value.trim().isEmpty() ? null : value.trim(); } private static final class PrintSource { private final DmsFileVersionEntity version; private final DmsFileObjectEntity object; private final DmsDocumentEntity document; private PrintSource(DmsFileVersionEntity version, DmsFileObjectEntity object, DmsDocumentEntity document) { this.version = version; this.object = object; this.document = document; } } }