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;
|
}
|
}
|