From 43aab58c9ecb1203770ba382efbdbae299ca87ec Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期一, 26 十月 2026 15:12:31 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_business' into dev_business

---
 yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/storage/SystemAgentImportBridgeService.java |  169 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 169 insertions(+), 0 deletions(-)

diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/storage/SystemAgentImportBridgeService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/storage/SystemAgentImportBridgeService.java
new file mode 100644
index 0000000..3d7629d
--- /dev/null
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/storage/SystemAgentImportBridgeService.java
@@ -0,0 +1,169 @@
+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));
+    }
+
+    /** 鍒犻櫎琛ュ綍闄勪欢鎵�灞炰細璇濓細闄勪欢鍏崇郴銆乥lob 璁板綍涓庣鐩樻枃浠朵竴骞舵竻鐞嗭紝闄勪欢涓嶅瓨鍦ㄦ椂骞傜瓑杩斿洖銆� */
+    @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");
+        }
+    }
+}

--
Gitblit v1.9.3