From 34981c30a78e8bbd7791131059a9210f9928b62c Mon Sep 17 00:00:00 2001
From: 刘光辉 <347230014@qq.com>
Date: 星期四, 17 九月 2026 09:24:09 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into master

---
 jnpf-dms/jnpf-dms-biz/src/main/java/jnpf/dmsService/DmsResourceService.java |  312 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 312 insertions(+), 0 deletions(-)

diff --git a/jnpf-dms/jnpf-dms-biz/src/main/java/jnpf/dmsService/DmsResourceService.java b/jnpf-dms/jnpf-dms-biz/src/main/java/jnpf/dmsService/DmsResourceService.java
new file mode 100644
index 0000000..7f675b8
--- /dev/null
+++ b/jnpf-dms/jnpf-dms-biz/src/main/java/jnpf/dmsService/DmsResourceService.java
@@ -0,0 +1,312 @@
+package jnpf.dmsService;
+
+import jnpf.base.UserInfo;
+import jnpf.dmsEntity.permission.entity.DmsDocumentEntity;
+import jnpf.dmsEntity.permission.entity.DmsFileVersionEntity;
+import jnpf.dmsEntity.permission.entity.DmsFolderEntity;
+import jnpf.dmsEntity.permission.entity.DmsPermissionResourceEntity;
+import jnpf.dmsEntity.permission.enums.DmsPermissionAction;
+import jnpf.dmsEntity.permission.enums.DmsResourceType;
+import jnpf.dmsEntity.permission.form.DocumentCreateForm;
+import jnpf.dmsEntity.permission.form.FileVersionCreateForm;
+import jnpf.dmsEntity.permission.form.FolderCreateForm;
+import jnpf.dmsEntity.permission.form.FolderCreateUserForm;
+import jnpf.dmsEntity.permission.vo.DmsPermissionResourceVO;
+import jnpf.dmsIntegration.audit.DmsAuditAdapter;
+import jnpf.dmsMapper.DmsDocumentMapper;
+import jnpf.dmsMapper.DmsFileVersionMapper;
+import jnpf.dmsMapper.DmsFolderMapper;
+import jnpf.dmsMapper.DmsPermissionResourceMapper;
+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;
+import java.util.UUID;
+
+@Service
+@RequiredArgsConstructor
+public class DmsResourceService {
+    private static final String WEB_FOLDER_REGISTRAR = "jnpf-dms-web";
+    private static final String WEB_FOLDER_SOURCE_PREFIX = "web-folder:";
+
+    private final DmsCurrentIdentity currentIdentity;
+    private final DmsPermissionDecisionService decisionService;
+    private final DmsFolderMapper folderMapper;
+    private final DmsDocumentMapper documentMapper;
+    private final DmsFileVersionMapper fileVersionMapper;
+    private final DmsPermissionResourceMapper resourceMapper;
+    private final DmsPermissionGrantService permissionGrantService;
+    private final DmsAuditAdapter auditAdapter;
+
+    @Transactional
+    public DmsPermissionResourceVO createFolder(FolderCreateUserForm form) {
+        UserInfo user = currentIdentity.requireUser();
+        String tenantId = currentIdentity.tenantId(user);
+        if (form.getParentId() == null) {
+            throw DmsPermissionException.of(DmsPermissionError.INVALID_RESOURCE_PARENT,
+                    "缃戦〉绔彧鑳藉湪宸叉湁 Folder 涓嬪垱寤哄瓙鐩綍");
+        }
+        decisionService.requireAllowed(DmsResourceType.FOLDER, form.getParentId(),
+                DmsPermissionAction.CREATE);
+
+        String sourceId = WEB_FOLDER_SOURCE_PREFIX + form.getSourceId().trim();
+        String name = form.getName().trim();
+        resourceMapper.lockSource(tenantId, "SYSTEM", sourceId);
+        DmsPermissionResourceEntity existingRegistry = resourceMapper.selectBySource(
+                tenantId, "SYSTEM", sourceId);
+        if (existingRegistry != null) {
+            DmsPermissionResourceVO existing = existingWebFolder(tenantId, user.getUserId(), form.getParentId(), name,
+                    sourceId, existingRegistry);
+            permissionGrantService.ensureFolderCreatorCreatePermission(
+                    tenantId, existingRegistry.getId(), user.getUserId());
+            return existing;
+        }
+
+        UUID folderId = resourceMapper.nextUuidV7();
+        DmsFolderEntity folder = new DmsFolderEntity();
+        folder.setId(folderId);
+        folder.setTenantId(tenantId);
+        folder.setParentId(form.getParentId());
+        folder.setName(name);
+        folder.setCreatedBy(user.getUserId());
+        try {
+            folderMapper.insert(folder);
+            insertRegistry(tenantId, folderId, DmsResourceType.FOLDER, sourceId,
+                    WEB_FOLDER_REGISTRAR);
+            permissionGrantService.ensureFolderCreatorCreatePermission(
+                    tenantId, folderId, user.getUserId());
+        } catch (DuplicateKeyException ex) {
+            throw conflict("Folder ID 鎴� source_id 宸茶鍏朵粬璧勬簮鍗犵敤", ex);
+        }
+        auditAdapter.dataChange("CREATE", "缃戦〉鍒涘缓 DMS 瀛愮洰褰�", "dms_folders",
+                folderId.toString(), null, null);
+        return requireView(tenantId, folderId);
+    }
+
+    @Transactional
+    public DmsPermissionResourceVO createFolder(FolderCreateForm form, String serviceId) {
+        UserInfo user = currentIdentity.requireUser();
+        String tenantId = currentIdentity.tenantId(user);
+        validateFolderMode(form);
+        if (form.getParentId() != null) {
+            decisionService.requireAllowed(DmsResourceType.FOLDER, form.getParentId(), DmsPermissionAction.CREATE);
+        }
+        DmsFolderEntity desired = new DmsFolderEntity();
+        desired.setId(form.getId());
+        desired.setTenantId(tenantId);
+        desired.setParentId(form.getParentId());
+        desired.setName(form.getName().trim());
+        desired.setCreatedBy(user.getUserId());
+
+        DmsPermissionResourceVO existing = existingFolder(desired, form.getSourceId(), serviceId);
+        if (existing != null) {
+            return existing;
+        }
+        try {
+            folderMapper.insert(desired);
+            insertRegistry(tenantId, form.getId(), DmsResourceType.FOLDER,
+                    form.getSourceId(), serviceId);
+        } catch (DuplicateKeyException ex) {
+            throw conflict("Folder ID銆佺埗绾с�佸悕绉版垨 source_id 宸茶鍏朵粬璧勬簮鍗犵敤", ex);
+        }
+        auditAdapter.dataChange("CREATE", "鍒涘缓 DMS Folder", "dms_folders", form.getId().toString(),
+                null, null);
+        return requireView(tenantId, form.getId());
+    }
+
+    @Transactional
+    public DmsPermissionResourceVO createDocument(DocumentCreateForm form, String serviceId) {
+        UserInfo user = currentIdentity.requireUser();
+        String tenantId = currentIdentity.tenantId(user);
+        decisionService.requireAllowed(DmsResourceType.FOLDER, form.getFolderId(), DmsPermissionAction.CREATE);
+        DmsDocumentEntity desired = new DmsDocumentEntity();
+        desired.setId(form.getId());
+        desired.setTenantId(tenantId);
+        desired.setFolderId(form.getFolderId());
+        desired.setName(form.getName().trim());
+        desired.setCreatedBy(user.getUserId());
+
+        DmsPermissionResourceVO existing = existingDocument(desired, form.getSourceId(), serviceId);
+        if (existing != null) {
+            return existing;
+        }
+        try {
+            documentMapper.insert(desired);
+            insertRegistry(tenantId, form.getId(), DmsResourceType.DOCUMENT,
+                    form.getSourceId(), serviceId);
+        } catch (DuplicateKeyException ex) {
+            throw conflict("Document ID銆佹墍灞� Folder 鎴� source_id 鍐茬獊", ex);
+        }
+        auditAdapter.dataChange("CREATE", "鍒涘缓 DMS Document", "dms_documents", form.getId().toString(),
+                null, null);
+        return requireView(tenantId, form.getId());
+    }
+
+    @Transactional
+    public DmsPermissionResourceVO createFileVersion(FileVersionCreateForm form, String serviceId) {
+        UserInfo user = currentIdentity.requireUser();
+        String tenantId = currentIdentity.tenantId(user);
+        decisionService.requireAllowed(DmsResourceType.DOCUMENT, form.getDocumentId(), DmsPermissionAction.UPDATE);
+        DmsFileVersionEntity desired = new DmsFileVersionEntity();
+        desired.setId(form.getId());
+        desired.setTenantId(tenantId);
+        desired.setDocumentId(form.getDocumentId());
+        desired.setVersionNo(form.getVersionNo());
+        desired.setFileName(form.getFileName().trim());
+        desired.setMimeType(trimToNull(form.getMimeType()));
+        desired.setSizeBytes(form.getSizeBytes());
+        desired.setChecksumSha256(form.getChecksumSha256() == null ? null
+                : form.getChecksumSha256().toLowerCase(Locale.ROOT));
+        desired.setCreatedBy(user.getUserId());
+
+        DmsPermissionResourceVO existing = existingFileVersion(desired, form.getSourceId(), serviceId);
+        if (existing != null) {
+            return existing;
+        }
+        try {
+            fileVersionMapper.insert(desired);
+            insertRegistry(tenantId, form.getId(), DmsResourceType.FILE_VERSION,
+                    form.getSourceId(), serviceId);
+        } catch (DuplicateKeyException ex) {
+            throw conflict("FileVersion ID銆佺増鏈彿鎴� source_id 鍐茬獊", ex);
+        }
+        auditAdapter.dataChange("CREATE", "鍒涘缓 DMS FileVersion", "dms_file_versions",
+                form.getId().toString(), null, null);
+        return requireView(tenantId, form.getId());
+    }
+
+    private DmsPermissionResourceVO existingFolder(DmsFolderEntity desired, String sourceId, String serviceId) {
+        DmsFolderEntity actual = folderMapper.selectByTenantAndId(desired.getTenantId(), desired.getId());
+        DmsPermissionResourceEntity registry = existingRegistry(desired.getTenantId(), desired.getId(), sourceId);
+        if (actual == null && registry == null) {
+            return null;
+        }
+        if (actual == null || registry == null || !Objects.equals(actual.getParentId(), desired.getParentId())
+                || !Objects.equals(actual.getName(), desired.getName())
+                || !sameRegistry(registry, DmsResourceType.FOLDER, sourceId, serviceId)) {
+            throw conflict("Folder 鍒涘缓閲嶈瘯鍐呭涓庡師璇锋眰涓嶄竴鑷�", null);
+        }
+        return requireView(desired.getTenantId(), desired.getId());
+    }
+
+    private DmsPermissionResourceVO existingWebFolder(String tenantId, String userId, UUID parentId,
+                                                       String name, String sourceId,
+                                                       DmsPermissionResourceEntity registry) {
+        DmsFolderEntity folder = folderMapper.selectByTenantAndId(tenantId, registry.getId());
+        if (folder == null
+                || !DmsResourceType.FOLDER.name().equals(registry.getResourceType())
+                || !"SYSTEM".equals(registry.getSourceType())
+                || !sourceId.equals(registry.getSourceId())
+                || !WEB_FOLDER_REGISTRAR.equals(registry.getRegisteredBy())
+                || !userId.equals(folder.getCreatedBy())
+                || !Objects.equals(parentId, folder.getParentId())
+                || !name.equals(folder.getName())) {
+            throw conflict("source_id 宸插瓨鍦紝浣嗗瓙鐩綍鍒涘缓鍐呭涓庡師璇锋眰涓嶄竴鑷�", null);
+        }
+        return requireView(tenantId, registry.getId());
+    }
+
+    private DmsPermissionResourceVO existingDocument(DmsDocumentEntity desired, String sourceId, String serviceId) {
+        DmsDocumentEntity actual = documentMapper.selectByTenantAndId(desired.getTenantId(), desired.getId());
+        DmsPermissionResourceEntity registry = existingRegistry(desired.getTenantId(), desired.getId(), sourceId);
+        if (actual == null && registry == null) {
+            return null;
+        }
+        if (actual == null || registry == null || !Objects.equals(actual.getFolderId(), desired.getFolderId())
+                || !Objects.equals(actual.getName(), desired.getName())
+                || !sameRegistry(registry, DmsResourceType.DOCUMENT, sourceId, serviceId)) {
+            throw conflict("Document 鍒涘缓閲嶈瘯鍐呭涓庡師璇锋眰涓嶄竴鑷�", null);
+        }
+        return requireView(desired.getTenantId(), desired.getId());
+    }
+
+    private DmsPermissionResourceVO existingFileVersion(DmsFileVersionEntity desired, String sourceId,
+                                                         String serviceId) {
+        DmsFileVersionEntity actual = fileVersionMapper.selectByTenantAndId(desired.getTenantId(), desired.getId());
+        DmsPermissionResourceEntity registry = existingRegistry(desired.getTenantId(), desired.getId(), sourceId);
+        if (actual == null && registry == null) {
+            return null;
+        }
+        if (actual == null || registry == null
+                || !Objects.equals(actual.getDocumentId(), desired.getDocumentId())
+                || !Objects.equals(actual.getVersionNo(), desired.getVersionNo())
+                || !Objects.equals(actual.getFileName(), desired.getFileName())
+                || !Objects.equals(actual.getMimeType(), desired.getMimeType())
+                || !Objects.equals(actual.getSizeBytes(), desired.getSizeBytes())
+                || !equalsIgnoreCase(actual.getChecksumSha256(), desired.getChecksumSha256())
+                || !sameRegistry(registry, DmsResourceType.FILE_VERSION, sourceId, serviceId)) {
+            throw conflict("FileVersion 鍒涘缓閲嶈瘯鍐呭涓庡師璇锋眰涓嶄竴鑷�", null);
+        }
+        return requireView(desired.getTenantId(), desired.getId());
+    }
+
+    private DmsPermissionResourceEntity existingRegistry(String tenantId, UUID id, String sourceId) {
+        DmsPermissionResourceEntity byId = resourceMapper.selectByTenantAndId(tenantId, id);
+        DmsPermissionResourceEntity bySource = resourceMapper.selectBySource(tenantId, "SYSTEM", sourceId.trim());
+        if (bySource != null && !id.equals(bySource.getId())) {
+            throw conflict("source_id 宸茬粦瀹氬埌鍙︿竴涓潈闄愯祫婧�", null);
+        }
+        return byId;
+    }
+
+    private boolean sameRegistry(DmsPermissionResourceEntity registry, DmsResourceType type,
+                                 String sourceId, String serviceId) {
+        return type.name().equals(registry.getResourceType())
+                && "SYSTEM".equals(registry.getSourceType())
+                && sourceId.trim().equals(registry.getSourceId())
+                && serviceId.equals(registry.getRegisteredBy());
+    }
+
+    private void insertRegistry(String tenantId, 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.trim());
+        registry.setRegisteredBy(serviceId);
+        resourceMapper.insert(registry);
+    }
+
+    private DmsPermissionResourceVO requireView(String tenantId, UUID id) {
+        DmsPermissionResourceVO view = resourceMapper.selectView(tenantId, id);
+        if (view == null) {
+            throw DmsPermissionException.of(DmsPermissionError.RESOURCE_NOT_FOUND,
+                    "鏉冮檺璧勬簮鍒涘缓鍚庢棤娉曡鍙�: " + id);
+        }
+        return view;
+    }
+
+    private void validateFolderMode(FolderCreateForm form) {
+        if (form.isRootInitialization() != (form.getParentId() == null)) {
+            throw DmsPermissionException.of(DmsPermissionError.INVALID_RESOURCE_PARENT,
+                    "浠� rootInitialization=true 鐨� Folder 鍙互涓嶆彁渚� parentId");
+        }
+    }
+
+    private String trimToNull(String value) {
+        if (value == null || value.trim().isEmpty()) {
+            return null;
+        }
+        return 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);
+        if (cause != null) {
+            exception.initCause(cause);
+        }
+        return exception;
+    }
+}

--
Gitblit v1.8.0