| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.system.service.storage; |
| | | |
| | | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; |
| | | import cn.iocoder.yudao.module.system.controller.admin.storage.vo.SystemStorageAttachmentSaveReqVO; |
| | | import cn.iocoder.yudao.module.system.dal.dataobject.storage.SystemAgentImportSessionDO; |
| | | import cn.iocoder.yudao.module.system.dal.dataobject.storage.SystemStorageAttachmentDO; |
| | | import cn.iocoder.yudao.module.system.dal.dataobject.storage.SystemStorageBlobDO; |
| | | import cn.iocoder.yudao.module.system.dal.mysql.storage.SystemAgentImportSessionMapper; |
| | | import cn.iocoder.yudao.module.system.dal.mysql.storage.SystemStorageAttachmentMapper; |
| | | import cn.iocoder.yudao.module.system.framework.agent.AgentBridgeException; |
| | | import cn.iocoder.yudao.module.system.framework.storage.config.StorageProperties; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Path; |
| | | import java.security.MessageDigest; |
| | | import java.security.NoSuchAlgorithmException; |
| | | import java.util.HexFormat; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | |
| | | @Service |
| | | public class SystemAgentImportBridgeService { |
| | | |
| | | public static final long MAX_FILE_BYTES = 8L * 1024 * 1024; |
| | | public static final String RECORD_TYPE = "agent_import_session"; |
| | | |
| | | @Resource |
| | | private SystemAgentImportSessionMapper sessionMapper; |
| | | @Resource |
| | | private SystemStorageAttachmentMapper attachmentMapper; |
| | | @Resource |
| | | private SystemStorageBlobService blobService; |
| | | @Resource |
| | | private SystemStorageAttachmentService attachmentService; |
| | | @Resource |
| | | private StorageProperties storageProperties; |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long createSession() { |
| | | Long userId = SecurityFrameworkUtils.getLoginUserId(); |
| | | if (userId == null) { |
| | | throw new AgentBridgeException(HttpStatus.UNAUTHORIZED, "UNAUTHORIZED", "æªç»å½æç»å½å·²è¿æ"); |
| | | } |
| | | SystemAgentImportSessionDO session = new SystemAgentImportSessionDO(); |
| | | session.setOwnerUserId(userId); |
| | | session.setStatus("active"); |
| | | sessionMapper.insert(session); |
| | | return session.getId(); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long bindAttachment(Long sessionId, Long blobId) { |
| | | requireOwnedSession(sessionId); |
| | | SystemStorageBlobDO blob = blobService.getStorageBlob(blobId); |
| | | requireOwnedBlob(blob); |
| | | SystemStorageAttachmentSaveReqVO req = new SystemStorageAttachmentSaveReqVO(); |
| | | req.setApplication("file"); |
| | | req.setRecordType(RECORD_TYPE); |
| | | req.setRecordId(sessionId); |
| | | SystemStorageAttachmentSaveReqVO.BlobItem item = new SystemStorageAttachmentSaveReqVO.BlobItem(); |
| | | item.setBlobId(blobId); |
| | | req.setBlobItems(List.of(item)); |
| | | attachmentService.bindAttachments(req); |
| | | // éç¨ bind 对åä¸ä¼è¯æ¯æ¿æ¢è¯ä¹ï¼éå¤ç»å®æ¶æ§éä»¶è¡ä¼è¢«å é¤ã |
| | | return attachmentMapper.selectListByApplicationAndRecordTypeAndRecordId("file", RECORD_TYPE, sessionId) |
| | | .stream().filter(row -> blobId.equals(row.getStorageBlobId())) |
| | | .map(SystemStorageAttachmentDO::getId).findFirst() |
| | | .orElseThrow(() -> new AgentBridgeException(HttpStatus.CONFLICT, "ATTACHMENT_BIND_FAILED", |
| | | "éä»¶ç»å®å¤±è´¥ï¼è¯·éæ°ä¸ä¼ ååè¯", true, null)); |
| | | } |
| | | |
| | | /** å é¤è¡¥å½éä»¶æå±ä¼è¯ï¼éä»¶å
³ç³»ãblob è®°å½ä¸ç£çæä»¶ä¸å¹¶æ¸
çï¼éä»¶ä¸å卿¶å¹çè¿åã */ |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void deleteImportAttachment(Long attachmentId) { |
| | | SystemStorageAttachmentDO attachment = attachmentMapper.selectById(attachmentId); |
| | | if (attachment == null) { |
| | | return; |
| | | } |
| | | if (!RECORD_TYPE.equals(attachment.getRecordType())) { |
| | | throw new AgentBridgeException(HttpStatus.FORBIDDEN, "ATTACHMENT_FORBIDDEN", "éä»¶ä¸å¯è®¿é®"); |
| | | } |
| | | Long sessionId = attachment.getRecordId(); |
| | | requireOwnedSession(sessionId); |
| | | // æä¼è¯æ´æ¹å é¤ï¼åä¸ä¼è¯å
è¢«æ¿æ¢çæ§éä»¶è¡ä¸ blob æä»¶é½ä¸ä¼æ®ç |
| | | attachmentService.deleteAttachmentsByRecordTypeAndRecordId(RECORD_TYPE, sessionId); |
| | | sessionMapper.deleteById(sessionId); |
| | | } |
| | | |
| | | public SystemAgentAttachmentRef requireOwnedAttachmentRef(Long attachmentId) { |
| | | SystemStorageAttachmentDO attachment = attachmentMapper.selectById(attachmentId); |
| | | if (attachment == null) { |
| | | throw new AgentBridgeException(HttpStatus.CONFLICT, "ATTACHMENT_REPLACED", |
| | | "é件已失æï¼å¯è½è¢«éæ°ä¸ä¼ æ¿æ¢ï¼ï¼è¯·éæ°ä¸ä¼ ååè¯", true, null); |
| | | } |
| | | if (!RECORD_TYPE.equals(attachment.getRecordType())) { |
| | | throw new AgentBridgeException(HttpStatus.FORBIDDEN, "ATTACHMENT_FORBIDDEN", "éä»¶ä¸å¯è®¿é®"); |
| | | } |
| | | requireOwnedSession(attachment.getRecordId()); |
| | | SystemStorageBlobDO blob = blobService.getStorageBlob(attachment.getStorageBlobId()); |
| | | requireOwnedBlob(blob); |
| | | Path file = resolveBlobFile(blob); |
| | | String filename = blob.getOriginalFilename(); |
| | | if (filename == null || filename.isBlank()) { |
| | | filename = "attachment"; |
| | | } |
| | | return new SystemAgentAttachmentRef(attachmentId, attachment.getRecordId(), filename, |
| | | blob.getByteSize(), file); |
| | | } |
| | | |
| | | public String sha256OfAttachment(Long attachmentId) { |
| | | SystemAgentAttachmentRef ref = requireOwnedAttachmentRef(attachmentId); |
| | | try (InputStream in = Files.newInputStream(ref.path())) { |
| | | MessageDigest digest = MessageDigest.getInstance("SHA-256"); |
| | | byte[] buffer = new byte[8192]; |
| | | long total = 0; |
| | | int read; |
| | | while ((read = in.read(buffer)) != -1) { |
| | | total += read; |
| | | if (total > MAX_FILE_BYTES) { |
| | | throw new AgentBridgeException(HttpStatus.FORBIDDEN, "FILE_TOO_LARGE", "éä»¶è¶
è¿ 8 MiB"); |
| | | } |
| | | digest.update(buffer, 0, read); |
| | | } |
| | | return HexFormat.of().formatHex(digest.digest()); |
| | | } catch (IOException ex) { |
| | | throw new AgentBridgeException(HttpStatus.FORBIDDEN, "ATTACHMENT_UNREADABLE", "éä»¶ä¸å¯è®¿é®"); |
| | | } catch (NoSuchAlgorithmException ex) { |
| | | throw new IllegalStateException("SHA-256 ä¸å¯ç¨", ex); |
| | | } |
| | | } |
| | | |
| | | private Path resolveBlobFile(SystemStorageBlobDO blob) { |
| | | try { |
| | | Path root = Path.of(storageProperties.getPath()).toRealPath(); |
| | | Path file = root.resolve(blob.getPath() == null ? "" : blob.getPath()) |
| | | .resolve(blob.getUidFilename()).toRealPath(); |
| | | if (!file.startsWith(root) || !Files.isRegularFile(file) |
| | | || Files.size(file) > MAX_FILE_BYTES) { |
| | | throw new AgentBridgeException(HttpStatus.FORBIDDEN, "ATTACHMENT_UNREADABLE", "éä»¶ä¸å¯è®¿é®"); |
| | | } |
| | | return file; |
| | | } catch (IOException ex) { |
| | | throw new AgentBridgeException(HttpStatus.FORBIDDEN, "ATTACHMENT_UNREADABLE", "éä»¶ä¸å¯è®¿é®"); |
| | | } |
| | | } |
| | | |
| | | private void requireOwnedSession(Long sessionId) { |
| | | SystemAgentImportSessionDO session = sessionMapper.selectById(sessionId); |
| | | Long userId = SecurityFrameworkUtils.getLoginUserId(); |
| | | if (session == null || userId == null || !Objects.equals(session.getOwnerUserId(), userId) |
| | | || !"active".equals(session.getStatus())) { |
| | | throw new AgentBridgeException(HttpStatus.FORBIDDEN, "SESSION_FORBIDDEN", "è¡¥å½ä¼è¯ä¸å¯è®¿é®"); |
| | | } |
| | | } |
| | | |
| | | private void requireOwnedBlob(SystemStorageBlobDO blob) { |
| | | Long userId = SecurityFrameworkUtils.getLoginUserId(); |
| | | if (blob == null || userId == null || !String.valueOf(userId).equals(blob.getCreator()) |
| | | || blob.getByteSize() == null || blob.getByteSize() > MAX_FILE_BYTES) { |
| | | throw new AgentBridgeException(HttpStatus.FORBIDDEN, "BLOB_FORBIDDEN", "æä»¶ä¸å¯è®¿é®æè¶
è¿ 8 MiB"); |
| | | } |
| | | } |
| | | } |