package cn.iocoder.yudao.module.system.controller.admin.agent; import cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService; 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.permission.PermissionService; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Comparator; import java.util.HexFormat; import java.util.List; import java.util.Set; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; /** 为智能体规划返回经过服务端裁剪的菜单、按钮、字段和数据范围能力。 */ @RestController @RequestMapping("/agent/bridge") public class SystemAgentCapabilityController { private static final List CATALOG = List.of( new CapabilityDefinition("mdm.item.import", "mdm.item", "import", "mdm:item:import", Set.of("code", "name", "specification", "categoryCode", "unitMeasureCode")), new CapabilityDefinition("srm.supplier.create", "srm.supplier", "create", "srm:supplier:create", Set.of("code", "name", "shortName", "type", "companyNature", "registeredCapital", "businessLicenseNo", "taxNo", "legalPerson", "address", "website", "contactName", "contactMobile", "contactEmail", "bankName", "bankAccount", "status", "remark")), new CapabilityDefinition("mes.bom.create", "mes.bom", "create", "mes:md-item:create", Set.of("parentCode", "componentCode", "quantity", "status", "remark")), new CapabilityDefinition("mes.qc-template.prepare", "mes.qc-template", "prepare", "mes:qc-template:create", Set.of("code", "name", "types", "indicatorCode", "standardValue", "thresholdMax", "thresholdMin", "unitCode", "itemCode", "remark")), new CapabilityDefinition("mes.mps.prepare", "mes.mps", "prepare", "mes:pro-mps:create", Set.of("productCode", "quantity", "unitCode", "requestDate", "saleOrderNo", "remark")), new CapabilityDefinition("mes.route.prepare", "mes.route", "prepare", "mes:pro-route:create", Set.of("routeCode", "routeName", "itemCode", "processCode", "sort", "yieldRate", "keyFlag", "remark")) ); @Resource private SystemAgentBridgeGuard bridgeGuard; @Resource private SecurityFrameworkService securityFrameworkService; @Resource private PermissionService permissionService; @GetMapping("/capabilities") public CommonResult capabilities( @RequestHeader(value = "X-Agent-Service-Token", required = false) String serviceToken) { AgentPrincipal principal = bridgeGuard.requireAuthenticated(serviceToken); DeptDataPermissionRespDTO dataScope = permissionService.getDeptDataPermission(principal.userId()); List capabilities = new ArrayList<>(); for (CapabilityDefinition definition : CATALOG) { if (securityFrameworkService.hasPermission(definition.permission())) { capabilities.add(new CapabilityResp(definition.tool(), definition.module(), definition.operation(), definition.fields().stream().sorted().toList(), scopeSummary(dataScope), true)); } } String policyRevision = digest(policyMaterial(principal.userId(), capabilities, dataScope)); String authorizationScope = "scope:" + digest(principal.userId() + ":" + policyRevision).substring(0, 32); return success(new CapabilityCatalogResp(policyRevision, authorizationScope, scopeSummary(dataScope), capabilities)); } private static String scopeSummary(DeptDataPermissionRespDTO scope) { if (Boolean.TRUE.equals(scope.getAll())) { return "全部授权数据"; } int departments = scope.getDeptIds() == null ? 0 : scope.getDeptIds().size(); if (Boolean.TRUE.equals(scope.getSelf()) && departments > 0) { return "本人及授权部门数据"; } if (Boolean.TRUE.equals(scope.getSelf())) { return "仅本人数据"; } return departments > 0 ? "授权部门数据" : "无部门数据范围"; } private static String policyMaterial(Long userId, List capabilities, DeptDataPermissionRespDTO scope) { List deptIds = scope.getDeptIds() == null ? List.of() : scope.getDeptIds().stream().sorted(Comparator.naturalOrder()).toList(); return userId + "|" + Boolean.TRUE.equals(scope.getAll()) + "|" + Boolean.TRUE.equals(scope.getSelf()) + "|" + deptIds + "|" + capabilities.stream().map(CapabilityResp::tool).sorted().toList(); } private static String digest(String value) { try { return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256") .digest(value.getBytes(StandardCharsets.UTF_8))); } catch (NoSuchAlgorithmException ex) { throw new IllegalStateException("JVM 缺少 SHA-256", ex); } } private record CapabilityDefinition(String tool, String module, String operation, String permission, Set fields) { } public record CapabilityResp(String tool, String module, String operation, List allowedFields, String scopeSummary, boolean canExecute) { } public record CapabilityCatalogResp(String policyRevision, String authorizationScope, String scopeSummary, List capabilities) { } }