云
4 小时以前 95ae0c83d2aa58d86b4039e3f7f16ee93afb8f00
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package cn.iocoder.yudao.module.system.controller.admin.agent;
 
import cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.system.framework.agent.AgentBridgeException;
import cn.iocoder.yudao.module.system.framework.agent.AgentDelegationToken;
import cn.iocoder.yudao.module.system.framework.agent.SystemAgentBridgeGuard;
import cn.iocoder.yudao.module.system.framework.agent.SystemAgentDelegationService;
import cn.iocoder.yudao.module.system.service.storage.SystemAgentImportBridgeService;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
import lombok.Data;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
 
import java.util.Map;
import java.util.LinkedHashSet;
import java.util.Set;
 
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 
/** 浏览器以当前登录身份为异步 Worker 签发短期、可撤销、限定范围的委托票据。 */
@RestController
@RequestMapping("/agent/bridge/delegations")
public class SystemAgentDelegationController {
 
    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')";
    private static final Set<String> ALLOWED_OPERATIONS = 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", "attachment.read", "import.modules.read", "import.preflight",
            "import.commit", "import.receipt.read");
    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 SystemAgentBridgeGuard bridgeGuard;
    @Resource
    private SystemAgentDelegationService delegationService;
    @Resource
    private SystemAgentImportBridgeService bridgeService;
    @Resource
    private PermissionCommonApi permissionApi;
 
    @PostMapping
    @PreAuthorize(IMPORT_PERMISSIONS_EXPR)
    public CommonResult<AgentDelegationToken> issue(
            @RequestHeader(value = "X-Agent-Service-Token", required = false) String serviceToken,
            @Valid @RequestBody IssueRequest request) {
        bridgeGuard.verifyServiceToken(serviceToken);
        if (!ALLOWED_OPERATIONS.containsAll(request.getOperations())) {
            throw new AgentBridgeException(HttpStatus.BAD_REQUEST, "DELEGATION_OPERATION_INVALID",
                    "委托包含不支持的操作");
        }
        Set<Long> attachmentIds = request.normalizedAttachmentIds();
        for (Long attachmentId : attachmentIds) {
            bridgeService.requireOwnedAttachmentRef(attachmentId);
        }
        String permission = resolvePermission(request.getModuleId());
        AgentDelegationToken token = delegationService.issue(request.getTaskId(), request.getModuleId(),
                attachmentIds, request.getOperations(), permission);
        return success(token);
    }
 
    @DeleteMapping("/{delegationId}")
    @PreAuthorize(IMPORT_PERMISSIONS_EXPR)
    public CommonResult<Boolean> revoke(
            @RequestHeader(value = "X-Agent-Service-Token", required = false) String serviceToken,
            @PathVariable String delegationId) {
        bridgeGuard.verifyServiceToken(serviceToken);
        delegationService.revoke(delegationId, SecurityFrameworkUtils.getLoginUserId());
        return success(true);
    }
 
    private String resolvePermission(String moduleId) {
        if (moduleId != null && !moduleId.isBlank()) {
            String permission = MODULE_PERMISSIONS.get(moduleId);
            if (permission == null) {
                throw new AgentBridgeException(HttpStatus.BAD_REQUEST, "MODULE_NOT_SUPPORTED", "不支持的导入模块");
            }
            return permission;
        }
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        return SystemAgentBridgeGuard.IMPORT_PERMISSIONS.stream()
                .filter(permission -> permissionApi.hasAnyPermissions(userId, permission))
                .findFirst()
                .orElseThrow(() -> new AgentBridgeException(HttpStatus.FORBIDDEN, "FORBIDDEN",
                        "当前用户缺少数据导入权限"));
    }
 
    @Data
    public static class IssueRequest {
        @NotBlank
        private String taskId;
        private String moduleId;
        private Long attachmentId;
        @Size(max = 20)
        private Set<Long> attachmentIds;
        @NotEmpty
        private Set<String> operations;
 
        public Set<Long> normalizedAttachmentIds() {
            Set<Long> values = new LinkedHashSet<>();
            if (attachmentIds != null) {
                values.addAll(attachmentIds);
            }
            if (attachmentId != null) {
                values.add(attachmentId);
            }
            if (values.stream().anyMatch(id -> id == null || id <= 0) || values.size() > 20) {
                throw new AgentBridgeException(HttpStatus.BAD_REQUEST, "DELEGATION_SCOPE_INVALID",
                        "委托附件范围无效");
            }
            return values;
        }
    }
}