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/controller/admin/agent/SystemAgentImportBridgeController.java | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 151 insertions(+), 0 deletions(-)
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/agent/SystemAgentImportBridgeController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/agent/SystemAgentImportBridgeController.java
new file mode 100644
index 0000000..f92a84e
--- /dev/null
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/agent/SystemAgentImportBridgeController.java
@@ -0,0 +1,151 @@
+package cn.iocoder.yudao.module.system.controller.admin.agent;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.system.framework.agent.AgentBridgeException;
+import cn.iocoder.yudao.module.system.framework.agent.AgentPrincipal;
+import cn.iocoder.yudao.module.system.framework.agent.SystemAgentBridgeGuard;
+import cn.iocoder.yudao.module.system.service.storage.SystemAgentAttachmentRef;
+import cn.iocoder.yudao.module.system.service.storage.SystemAgentImportBridgeService;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Map;
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+/** Read-only bridge for material onboarding; business writes live in the MDM module. */
+@RestController
+@RequestMapping("/agent/bridge")
+public class SystemAgentImportBridgeController {
+
+ private static final Set<String> ACTIONS = Set.of("material_import.prepare", "material_import.read",
+ "material_import.review", "material_import.preflight", "material_import.commit",
+ "material_import.read_receipt",
+ "conversation.create", "conversation.read", "conversation.message",
+ "conversation.action", "conversation.report");
+
+ /** module_id 鈫� 涓氬姟瀵煎叆鏉冮檺锛汸ython 浠ユā鍧� ID 浣滀负鎺堟潈 resource銆� */
+ private static final Map<String, String> MODULE_PERMISSIONS = Map.of(
+ "mdm.item", SystemAgentBridgeGuard.PERMISSION_ITEM_IMPORT,
+ "srm.supplier", "srm:supplier:create",
+ "mes.bom", "mes:md-item:create",
+ "mes.qc-template", "mes:qc-template:create",
+ "mes.mps", "mes:pro-mps:create",
+ "mes.route", "mes:pro-route:create");
+
+ @Resource
+ private SystemAgentImportBridgeService bridgeService;
+ @Resource
+ private SystemAgentBridgeGuard bridgeGuard;
+
+ private static final String IMPORT_PERMISSIONS_EXPR =
+ "@ss.hasAnyPermissions('mdm:item:import','srm:supplier:create','mes:md-item:create',"
+ + "'mes:qc-template:create','mes:pro-mps:create','mes:pro-route:create')";
+
+ @PostMapping("/import-sessions")
+ @PreAuthorize(IMPORT_PERMISSIONS_EXPR)
+ public CommonResult<Long> createSession() {
+ return success(bridgeService.createSession());
+ }
+
+ @PostMapping("/import-sessions/{sessionId}/attachments")
+ @PreAuthorize(IMPORT_PERMISSIONS_EXPR)
+ public CommonResult<Long> bindAttachment(@PathVariable Long sessionId, @Valid @RequestBody BindRequest request) {
+ return success(bridgeService.bindAttachment(sessionId, request.getBlobId()));
+ }
+
+ @DeleteMapping("/attachments/{attachmentId}")
+ public CommonResult<Boolean> deleteAttachment(
+ @RequestHeader(value = "X-Agent-Service-Token", required = false) String serviceToken,
+ @RequestHeader(value = "X-Agent-Delegation-Token", required = false) String delegationToken,
+ @RequestHeader(value = "X-Agent-Task-Id", required = false) String taskId,
+ @PathVariable Long attachmentId) {
+ bridgeGuard.requireAny(serviceToken, delegationToken, taskId, "attachment.delete", null, attachmentId);
+ bridgeService.deleteImportAttachment(attachmentId);
+ return success(true);
+ }
+
+ @PostMapping("/authorize")
+ public CommonResult<Map<String, String>> authorize(
+ @RequestHeader(value = "X-Agent-Service-Token", required = false) String serviceToken,
+ @RequestHeader(value = "X-Agent-Delegation-Token", required = false) String delegationToken,
+ @RequestHeader(value = "X-Agent-Task-Id", required = false) String taskId,
+ @Valid @RequestBody AuthorizeRequest request) {
+ if (!ACTIONS.contains(request.getAction())) {
+ throw new AgentBridgeException(HttpStatus.FORBIDDEN, "ACTION_FORBIDDEN", "涓嶆敮鎸佺殑鏅鸿兘浣撴搷浣�");
+ }
+ AgentPrincipal principal;
+ if (request.getAction().startsWith("conversation.")) {
+ String permission = MODULE_PERMISSIONS.get(request.getResource());
+ principal = permission == null
+ ? bridgeGuard.requireAny(serviceToken, delegationToken, taskId, request.getAction(),
+ request.getResource(), null)
+ : bridgeGuard.require(serviceToken, delegationToken, taskId, request.getAction(), permission,
+ request.getResource(), null);
+ } else {
+ principal = bridgeGuard.require(serviceToken, delegationToken, taskId, request.getAction(),
+ SystemAgentBridgeGuard.PERMISSION_ITEM_IMPORT, "mdm.item",
+ requiresAttachmentOwnership(request.getAction()) ? parseId(request.getResource()) : null);
+ }
+ if (requiresAttachmentOwnership(request.getAction())) {
+ bridgeService.requireOwnedAttachmentRef(parseId(request.getResource()));
+ }
+ // read/review/read_receipt 鐨勪换鍔� UUID 鎵�鏈夋潈鐢� Python checkpoint 浣滅敤鍩熸牳楠岋紱鏈閲嶆煡褰撳墠鎸夐挳鏉冮檺銆�
+ return success(Map.of("user_id", String.valueOf(principal.userId()),
+ "policy_revision", principal.policyRevision()));
+ }
+
+ @GetMapping("/attachments/{attachmentId}")
+ public ResponseEntity<FileSystemResource> downloadAttachment(
+ @RequestHeader(value = "X-Agent-Service-Token", required = false) String serviceToken,
+ @RequestHeader(value = "X-Agent-Delegation-Token", required = false) String delegationToken,
+ @RequestHeader(value = "X-Agent-Task-Id", required = false) String taskId,
+ @PathVariable Long attachmentId) {
+ bridgeGuard.requireAny(serviceToken, delegationToken, taskId, "attachment.read", null, attachmentId);
+ SystemAgentAttachmentRef ref = bridgeService.requireOwnedAttachmentRef(attachmentId);
+ return ResponseEntity.ok().contentType(MediaType.APPLICATION_OCTET_STREAM)
+ .contentLength(ref.byteSize())
+ .header("X-Agent-Filename-B64", Base64.getEncoder().encodeToString(
+ ref.originalFilename().getBytes(StandardCharsets.UTF_8)))
+ .body(new FileSystemResource(ref.path()));
+ }
+
+ private static boolean requiresAttachmentOwnership(String action) {
+ return "material_import.prepare".equals(action)
+ || "material_import.preflight".equals(action)
+ || "material_import.commit".equals(action);
+ }
+
+ private Long parseId(String value) {
+ try {
+ return Long.valueOf(value);
+ } catch (NumberFormatException ex) {
+ throw new AgentBridgeException(HttpStatus.FORBIDDEN, "ATTACHMENT_FORBIDDEN", "闄勪欢涓嶅彲璁块棶");
+ }
+ }
+
+ @Data
+ public static class BindRequest {
+ @NotNull
+ private Long blobId;
+ }
+
+ @Data
+ public static class AuthorizeRequest {
+ @NotNull
+ private String action;
+ @NotNull
+ private String resource;
+ }
+}
--
Gitblit v1.9.3