reportResourcesAndGetResult(@RequestBody ReportResourceParam reportResourceReq) {
+ return resourceReportApi.reportResourcesAndGetResult(reportResourceReq);
+ }
+
+}
diff --git a/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/entity/SysResource.java b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/entity/SysResource.java
new file mode 100644
index 000000000..2ce7ab314
--- /dev/null
+++ b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/entity/SysResource.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright [2020-2030] [https://www.stylefeng.cn]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Guns采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Guns源码头部的版权声明。
+ * 3.请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://gitee.com/stylefeng/guns
+ * 5.在修改包名,模块名称,项目代码等时,请注明软件出处 https://gitee.com/stylefeng/guns
+ * 6.若您的项目无法满足以上几点,可申请商业授权
+ */
+package cn.stylefeng.roses.kernel.sys.modular.resource.entity;
+
+import cn.stylefeng.roses.kernel.db.api.pojo.entity.BaseEntity;
+import cn.stylefeng.roses.kernel.rule.annotation.ChineseDescription;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 资源表
+ *
+ * @author fengshuonan
+ * @since 2020/11/23 22:45
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_resource")
+public class SysResource extends BaseEntity {
+
+ /**
+ * 资源id
+ */
+ @TableId("resource_id")
+ @ChineseDescription("资源id")
+ private Long resourceId;
+
+ /**
+ * 应用编码
+ */
+ @TableField("app_code")
+ @ChineseDescription("应用编码")
+ private String appCode;
+
+ /**
+ * 资源编码
+ */
+ @TableField("resource_code")
+ @ChineseDescription("资源编码")
+ private String resourceCode;
+
+ /**
+ * 资源名称
+ */
+ @TableField("resource_name")
+ @ChineseDescription("资源名称")
+ private String resourceName;
+
+ /**
+ * 项目编码
+ */
+ @TableField("project_code")
+ @ChineseDescription("项目编码")
+ private String projectCode;
+
+ /**
+ * 类名称
+ */
+ @TableField("class_name")
+ @ChineseDescription("类名称")
+ private String className;
+
+ /**
+ * 方法名称
+ */
+ @TableField("method_name")
+ @ChineseDescription("方法名称")
+ private String methodName;
+
+ /**
+ * 资源模块编码
+ */
+ @TableField("modular_code")
+ @ChineseDescription("资源模块编码")
+ private String modularCode;
+
+ /**
+ * 资源模块名称
+ */
+ @TableField("modular_name")
+ @ChineseDescription("资源模块名称")
+ private String modularName;
+
+ /**
+ * 资源初始化的服务器ip地址
+ */
+ @TableField("ip_address")
+ @ChineseDescription("资源初始化的服务器ip地址")
+ private String ipAddress;
+
+ /**
+ * 是否是视图类型:Y-是,N-否
+ * 如果是视图类型,url需要以 '/view' 开头,
+ * 视图类型的接口会渲染出html界面,而不是json数据,
+ * 视图层一般会在前后端不分离项目出现
+ */
+ @TableField("view_flag")
+ @ChineseDescription("是否是视图类型")
+ private String viewFlag;
+
+ /**
+ * 资源url
+ */
+ @TableField("url")
+ @ChineseDescription("资源url")
+ private String url;
+
+ /**
+ * http请求方法
+ */
+ @TableField("http_method")
+ @ChineseDescription("http请求方法")
+ private String httpMethod;
+
+ /**
+ * 资源的业务类型:1-业务类型,2-系统类型
+ */
+ @TableField("resource_biz_type")
+ @ChineseDescription("资源的业务类型:1-业务类型,2-系统类型")
+ private Integer resourceBizType;
+
+ /**
+ * 是否需要登录:Y-是,N-否
+ */
+ @TableField("required_login_flag")
+ @ChineseDescription("是否需要登录:Y-是,N-否")
+ private String requiredLoginFlag;
+
+ /**
+ * 是否需要鉴权:Y-是,N-否
+ */
+ @TableField("required_permission_flag")
+ @ChineseDescription("是否需要鉴权:Y-是,N-否")
+ private String requiredPermissionFlag;
+
+ /**
+ * 需要进行参数校验的分组
+ *
+ * json形式存储
+ */
+ @TableField("validate_groups")
+ @ChineseDescription("需要进行参数校验的分组")
+ private String validateGroups;
+
+ /**
+ * 接口参数的字段描述
+ *
+ * json形式存储
+ */
+ @TableField("param_field_descriptions")
+ @ChineseDescription("接口参数的字段描述")
+ private String paramFieldDescriptions;
+
+ /**
+ * 接口返回结果的字段描述
+ *
+ * json形式存储
+ */
+ @TableField("response_field_descriptions")
+ @ChineseDescription("接口返回结果的字段描述")
+ private String responseFieldDescriptions;
+
+ /**
+ * 应用名称
+ */
+ private transient String appName;
+
+}
diff --git a/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/factory/ResourceFactory.java b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/factory/ResourceFactory.java
new file mode 100644
index 000000000..8af52198b
--- /dev/null
+++ b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/factory/ResourceFactory.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright [2020-2030] [https://www.stylefeng.cn]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Guns采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Guns源码头部的版权声明。
+ * 3.请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://gitee.com/stylefeng/guns
+ * 5.在修改包名,模块名称,项目代码等时,请注明软件出处 https://gitee.com/stylefeng/guns
+ * 6.若您的项目无法满足以上几点,可申请商业授权
+ */
+package cn.stylefeng.roses.kernel.sys.modular.resource.factory;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.bean.copier.CopyOptions;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.stylefeng.roses.kernel.rule.enums.YesOrNotEnum;
+import cn.stylefeng.roses.kernel.scanner.api.pojo.resource.FieldMetadata;
+import cn.stylefeng.roses.kernel.scanner.api.pojo.resource.ResourceDefinition;
+import cn.stylefeng.roses.kernel.sys.modular.resource.entity.SysResource;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.parser.Feature;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * ResourceDefinition和SysResource实体互转
+ *
+ * @author fengshuonan
+ * @since 2019-05-29-14:37
+ */
+public class ResourceFactory {
+
+ /**
+ * ResourceDefinition转化为SysResource实体
+ *
+ * @author fengshuonan
+ * @since 2020/12/9 14:14
+ */
+ public static SysResource createResource(ResourceDefinition resourceDefinition) {
+ SysResource resource = new SysResource();
+ BeanUtil.copyProperties(resourceDefinition, resource, CopyOptions.create().ignoreError());
+ resource.setResourceCode(resourceDefinition.getResourceCode());
+
+ if (resourceDefinition.getRequiredLoginFlag()) {
+ resource.setRequiredLoginFlag(YesOrNotEnum.Y.name());
+ } else {
+ resource.setRequiredLoginFlag(YesOrNotEnum.N.name());
+ }
+
+ if (resourceDefinition.getRequiredPermissionFlag()) {
+ resource.setRequiredPermissionFlag(YesOrNotEnum.Y.name());
+ } else {
+ resource.setRequiredPermissionFlag(YesOrNotEnum.N.name());
+ }
+
+ if (resourceDefinition.getViewFlag()) {
+ resource.setViewFlag(YesOrNotEnum.Y.name());
+ } else {
+ resource.setViewFlag(YesOrNotEnum.N.name());
+ }
+
+ // 转化校验组
+ if (ObjectUtil.isNotEmpty(resourceDefinition.getValidateGroups())) {
+ resource.setValidateGroups(JSON.toJSONString(resourceDefinition.getValidateGroups(), SerializerFeature.WriteClassName));
+ }
+
+ // 转化接口参数的字段描述
+ if (ObjectUtil.isNotEmpty(resourceDefinition.getParamFieldDescriptions())) {
+ resource.setParamFieldDescriptions(JSON.toJSONString(resourceDefinition.getParamFieldDescriptions(), SerializerFeature.WriteClassName));
+ }
+
+ // 转化接口返回结果的字段描述
+ if (ObjectUtil.isNotEmpty(resourceDefinition.getResponseFieldDescriptions())) {
+ resource.setResponseFieldDescriptions(JSON.toJSONString(resourceDefinition.getResponseFieldDescriptions(), SerializerFeature.WriteClassName));
+ }
+
+ return resource;
+ }
+
+ /**
+ * SysResource实体转化为ResourceDefinition对象
+ *
+ * @author fengshuonan
+ * @since 2020/12/9 14:15
+ */
+ public static ResourceDefinition createResourceDefinition(SysResource sysResource) {
+
+ ResourceDefinition resourceDefinition = new ResourceDefinition();
+
+ // 拷贝公共属性
+ BeanUtil.copyProperties(sysResource, resourceDefinition, CopyOptions.create().ignoreError());
+
+ // 设置是否需要登录标识,Y为需要登录
+ resourceDefinition.setRequiredLoginFlag(YesOrNotEnum.Y.name().equals(sysResource.getRequiredLoginFlag()));
+
+ // 设置是否需要权限认证标识,Y为需要权限认证
+ resourceDefinition.setRequiredPermissionFlag(YesOrNotEnum.Y.name().equals(sysResource.getRequiredPermissionFlag()));
+
+ // 设置是否是视图类型
+ resourceDefinition.setViewFlag(YesOrNotEnum.Y.name().equals(sysResource.getViewFlag()));
+
+ // 转化校验组
+ if (ObjectUtil.isNotEmpty(sysResource.getValidateGroups())) {
+ resourceDefinition.setValidateGroups(JSON.parseObject(sysResource.getValidateGroups(), Set.class, Feature.SupportAutoType));
+ }
+
+ // 转化接口参数的字段描述
+ if (ObjectUtil.isNotEmpty(sysResource.getParamFieldDescriptions())) {
+ resourceDefinition.setParamFieldDescriptions(JSON.parseObject(sysResource.getParamFieldDescriptions(), Set.class, Feature.SupportAutoType));
+ }
+
+ // 转化接口返回结果的字段描述
+ if (ObjectUtil.isNotEmpty(sysResource.getResponseFieldDescriptions())) {
+ resourceDefinition.setResponseFieldDescriptions(JSON.parseObject(sysResource.getResponseFieldDescriptions(), FieldMetadata.class, Feature.SupportAutoType));
+ }
+
+ return resourceDefinition;
+ }
+
+ /**
+ * ResourceDefinition转化为api界面的详情信息
+ *
+ * @author fengshuonan
+ * @since 2021/1/16 16:09
+ */
+ public static ResourceDefinition fillResourceDetail(ResourceDefinition resourceDefinition) {
+
+ // 这个接口的校验组信息
+ Set validateGroups = resourceDefinition.getValidateGroups();
+
+ // 接口的请求参数信息
+ Set paramFieldDescriptions = resourceDefinition.getParamFieldDescriptions();
+ if(paramFieldDescriptions != null && paramFieldDescriptions.size() > 0){
+ for (FieldMetadata fieldMetadata : paramFieldDescriptions) {
+ fillDetailMessage(validateGroups, fieldMetadata);
+ }
+ }
+
+ // 接口的响应参数信息
+ FieldMetadata responseFieldDescriptions = resourceDefinition.getResponseFieldDescriptions();
+ fillDetailMessage(validateGroups, responseFieldDescriptions);
+
+ return resourceDefinition;
+ }
+
+ /**
+ * 填充字段里详细的提示信息
+ *
+ * @author fengshuonan
+ * @since 2021/1/16 18:00
+ */
+ public static void fillDetailMessage(Set validateGroups, FieldMetadata fieldMetadata) {
+ if (validateGroups == null || validateGroups.isEmpty()) {
+ return;
+ }
+
+ if (fieldMetadata == null) {
+ return;
+ }
+ StringBuilder finalValidateMessages = new StringBuilder();
+ Map> groupAnnotations = fieldMetadata.getGroupValidationMessage();
+ if (groupAnnotations != null) {
+ for (String validateGroup : validateGroups) {
+ Set validateMessage = groupAnnotations.get(validateGroup);
+ if (validateMessage != null && !validateMessage.isEmpty()) {
+ finalValidateMessages.append(StrUtil.join(",", validateMessage));
+ }
+ }
+ }
+ fieldMetadata.setValidationMessages(finalValidateMessages.toString());
+
+ // 递归填充子类型的详细提示信息
+ if (fieldMetadata.getGenericFieldMetadata() != null && !fieldMetadata.getGenericFieldMetadata().isEmpty()) {
+ for (FieldMetadata metadata : fieldMetadata.getGenericFieldMetadata()) {
+ fillDetailMessage(validateGroups, metadata);
+ }
+ }
+ }
+
+ /**
+ * 将资源的集合整理成一个map,key是url,value是ResourceDefinition
+ *
+ * @author fengshuonan
+ * @since 2021/5/17 16:21
+ */
+ public static Map orderedResourceDefinition(List sysResourceList) {
+
+ HashMap result = new HashMap<>();
+
+ if (ObjectUtil.isEmpty(sysResourceList)) {
+ return result;
+ }
+
+ for (ResourceDefinition sysResource : sysResourceList) {
+ String url = sysResource.getUrl();
+ result.put(url, sysResource);
+ }
+
+ return result;
+ }
+
+}
diff --git a/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/mapper/SysResourceMapper.java b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/mapper/SysResourceMapper.java
new file mode 100644
index 000000000..cbd0fadd5
--- /dev/null
+++ b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/mapper/SysResourceMapper.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright [2020-2030] [https://www.stylefeng.cn]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Guns采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Guns源码头部的版权声明。
+ * 3.请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://gitee.com/stylefeng/guns
+ * 5.在修改包名,模块名称,项目代码等时,请注明软件出处 https://gitee.com/stylefeng/guns
+ * 6.若您的项目无法满足以上几点,可申请商业授权
+ */
+package cn.stylefeng.roses.kernel.sys.modular.resource.mapper;
+
+
+import cn.stylefeng.roses.kernel.db.mp.injector.CustomBaseMapper;
+import cn.stylefeng.roses.kernel.sys.modular.resource.entity.SysResource;
+
+/**
+ * 资源表 Mapper 接口
+ *
+ * @author fengshuonan
+ * @since 2020/11/23 22:45
+ */
+public interface SysResourceMapper extends CustomBaseMapper {
+
+}
diff --git a/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/mapper/mapping/SysResourceMapper.xml b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/mapper/mapping/SysResourceMapper.xml
new file mode 100644
index 000000000..392f2e62a
--- /dev/null
+++ b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/mapper/mapping/SysResourceMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/pojo/ResourceTreeNode.java b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/pojo/ResourceTreeNode.java
new file mode 100644
index 000000000..152774106
--- /dev/null
+++ b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/pojo/ResourceTreeNode.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright [2020-2030] [https://www.stylefeng.cn]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Guns采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Guns源码头部的版权声明。
+ * 3.请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://gitee.com/stylefeng/guns
+ * 5.在修改包名,模块名称,项目代码等时,请注明软件出处 https://gitee.com/stylefeng/guns
+ * 6.若您的项目无法满足以上几点,可申请商业授权
+ */
+package cn.stylefeng.roses.kernel.sys.modular.resource.pojo;
+
+import cn.stylefeng.roses.kernel.rule.annotation.ChineseDescription;
+import cn.stylefeng.roses.kernel.rule.tree.factory.base.AbstractTreeNode;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 资源树节点的描述
+ *
+ * @author fengshuonan
+ * @since 2020/3/26 14:29
+ */
+@Data
+public class ResourceTreeNode implements AbstractTreeNode {
+
+ /**
+ * 资源id
+ */
+ @ChineseDescription("资源id")
+ private String code;
+
+ /**
+ * 父级资源id
+ */
+ @ChineseDescription("父级资源id")
+ private String parentCode;
+
+ /**
+ * 资源名称
+ */
+ @ChineseDescription("资源名称")
+ private String nodeName;
+
+ /**
+ * 是否是资源标识
+ *
+ * true-是资源标识
+ * false-虚拟节点,不是一个具体资源
+ */
+ @ChineseDescription("是否是资源标识")
+ private Boolean resourceFlag;
+
+ /**
+ * 能否选择
+ */
+ @ChineseDescription("能否选择")
+ private Boolean checked;
+
+ /**
+ * 是否是半开状态(一部分选中)
+ */
+ @ChineseDescription("是否是半开状态(一部分选中)")
+ private Boolean indeterminate;
+
+ /**
+ * 子节点集合
+ */
+ @ChineseDescription("子节点集合")
+ private List children;
+
+ @Override
+ public String getNodeId() {
+ return this.code;
+ }
+
+ @Override
+ public String getNodeParentId() {
+ return this.parentCode;
+ }
+
+ @Override
+ public void setChildrenNodes(List childrenNodes) {
+ this.children = childrenNodes;
+ }
+}
diff --git a/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/service/SysResourceService.java b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/service/SysResourceService.java
new file mode 100644
index 000000000..63c0535b5
--- /dev/null
+++ b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/service/SysResourceService.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright [2020-2030] [https://www.stylefeng.cn]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Guns采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Guns源码头部的版权声明。
+ * 3.请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://gitee.com/stylefeng/guns
+ * 5.在修改包名,模块名称,项目代码等时,请注明软件出处 https://gitee.com/stylefeng/guns
+ * 6.若您的项目无法满足以上几点,可申请商业授权
+ */
+package cn.stylefeng.roses.kernel.sys.modular.resource.service;
+
+import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
+import cn.stylefeng.roses.kernel.scanner.api.pojo.resource.ResourceDefinition;
+import cn.stylefeng.roses.kernel.sys.modular.resource.entity.SysResource;
+import cn.stylefeng.roses.kernel.sys.modular.resource.pojo.ResourceTreeNode;
+import com.baomidou.mybatisplus.extension.service.IService;
+import jdk.management.resource.ResourceRequest;
+
+import java.util.List;
+
+/**
+ * 资源服务类
+ *
+ * @author fengshuonan
+ * @since 2020/11/24 19:56
+ */
+public interface SysResourceService extends IService {
+
+ /**
+ * 获取资源分页列表
+ *
+ * @param resourceRequest 请求参数
+ * @return 返回结果
+ * @author fengshuonan
+ * @since 2020/11/24 20:45
+ */
+ PageResult findPage(ResourceRequest resourceRequest);
+
+ /**
+ * 通过应用code获取获取资源下拉列表
+ *
+ * 只获取菜单资源
+ *
+ * @param resourceRequest 请求参数
+ * @return 响应下拉结果
+ * @author fengshuonan
+ * @since 2020/11/24 20:45
+ */
+ List findList(ResourceRequest resourceRequest);
+
+ /**
+ * 获取角色绑定的资源树列表,用于分配接口权限
+ *
+ * @param roleId 角色id
+ * @param treeBuildFlag true-带树形结构,false-不组装树形结构的
+ * @param resourceBizType 资源的类型,1-业务类,2-系统类
+ * @author fengshuonan
+ * @since 2022/9/28 23:46
+ */
+ List getRoleResourceTree(Long roleId, Boolean treeBuildFlag, Integer resourceBizType);
+
+ /**
+ * 获取资源绑定列表(业务通用)
+ *
+ * @param resourceCodes 业务已经绑定的资源的编码集合
+ * @param treeBuildFlag 是否要构建成树
+ * @param resourceBizType 资源的类型,1-业务类,2-系统类
+ * @author fengshuonan
+ * @since 2021/8/8 22:24
+ */
+ List getResourceList(List resourceCodes, Boolean treeBuildFlag, Integer resourceBizType);
+
+ /**
+ * 获取资源的详情
+ *
+ * @param resourceRequest 请求参数
+ * @return 资源详情
+ * @author fengshuonan
+ * @since 2020/12/18 16:04
+ */
+ ResourceDefinition getApiResourceDetail(ResourceRequest resourceRequest);
+
+ /**
+ * 删除某个项目的所有资源
+ *
+ * @param projectCode 项目编码,一般为spring application name
+ * @author fengshuonan
+ * @since 2020/11/24 20:46
+ */
+ void deleteResourceByProjectCode(String projectCode);
+
+ /**
+ * 更新资源编码前缀,将guns$前缀改为新的
+ *
+ * @author fengshuonan
+ * @since 2022/11/17 0:17
+ */
+ void updateResourceAppCode(String newAppCode);
+
+}
diff --git a/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/service/impl/SysResourceServiceImpl.java b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/service/impl/SysResourceServiceImpl.java
new file mode 100644
index 000000000..ea22ad820
--- /dev/null
+++ b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/service/impl/SysResourceServiceImpl.java
@@ -0,0 +1,577 @@
+/*
+ * Copyright [2020-2030] [https://www.stylefeng.cn]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Guns采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Guns源码头部的版权声明。
+ * 3.请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://gitee.com/stylefeng/guns
+ * 5.在修改包名,模块名称,项目代码等时,请注明软件出处 https://gitee.com/stylefeng/guns
+ * 6.若您的项目无法满足以上几点,可申请商业授权
+ */
+package cn.stylefeng.roses.kernel.sys.modular.resource.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.ObjectUtil;
+import cn.stylefeng.roses.kernel.auth.api.LoginUserApi;
+import cn.stylefeng.roses.kernel.auth.api.context.LoginContext;
+import cn.stylefeng.roses.kernel.auth.api.pojo.login.basic.SimpleRoleInfo;
+import cn.stylefeng.roses.kernel.cache.api.CacheOperatorApi;
+import cn.stylefeng.roses.kernel.db.api.context.DbOperatorContext;
+import cn.stylefeng.roses.kernel.db.api.factory.PageFactory;
+import cn.stylefeng.roses.kernel.db.api.factory.PageResultFactory;
+import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
+import cn.stylefeng.roses.kernel.rule.constants.RuleConstants;
+import cn.stylefeng.roses.kernel.rule.constants.TreeConstants;
+import cn.stylefeng.roses.kernel.rule.enums.DbTypeEnum;
+import cn.stylefeng.roses.kernel.rule.enums.ResBizTypeEnum;
+import cn.stylefeng.roses.kernel.rule.enums.YesOrNotEnum;
+import cn.stylefeng.roses.kernel.rule.tree.factory.DefaultTreeBuildFactory;
+import cn.stylefeng.roses.kernel.rule.util.GunsResourceCodeUtil;
+import cn.stylefeng.roses.kernel.scanner.api.ResourceReportApi;
+import cn.stylefeng.roses.kernel.scanner.api.pojo.resource.ReportResourceParam;
+import cn.stylefeng.roses.kernel.scanner.api.pojo.resource.ResourceDefinition;
+import cn.stylefeng.roses.kernel.scanner.api.pojo.resource.ResourceUrlParam;
+import cn.stylefeng.roses.kernel.scanner.api.pojo.resource.SysResourcePersistencePojo;
+import cn.stylefeng.roses.kernel.sys.modular.resource.entity.SysResource;
+import cn.stylefeng.roses.kernel.sys.modular.resource.factory.ResourceFactory;
+import cn.stylefeng.roses.kernel.sys.modular.resource.mapper.SysResourceMapper;
+import cn.stylefeng.roses.kernel.sys.modular.resource.pojo.ResourceTreeNode;
+import cn.stylefeng.roses.kernel.sys.modular.resource.service.SysResourceService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import jdk.management.resource.ResourceRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.RequestBody;
+
+import javax.annotation.Resource;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 资源表 服务实现类
+ *
+ * @author fengshuonan
+ * @since 2020/11/23 22:45
+ */
+@Service
+@Slf4j
+public class SysResourceServiceImpl extends ServiceImpl implements SysResourceService, ResourceReportApi, ResourceServiceApi {
+
+ @Resource
+ private SysResourceMapper resourceMapper;
+
+ @Resource
+ private RoleServiceApi roleServiceApi;
+
+ @Resource(name = "resourceCache")
+ private CacheOperatorApi resourceCache;
+
+ @Override
+ public PageResult findPage(ResourceRequest resourceRequest) {
+ LambdaQueryWrapper wrapper = createWrapper(resourceRequest);
+ Page page = this.page(PageFactory.defaultPage(), wrapper);
+ return PageResultFactory.createPageResult(page);
+ }
+
+ @Override
+ public List findList(ResourceRequest resourceRequest) {
+
+ LambdaQueryWrapper wrapper = createWrapper(resourceRequest);
+
+ // 只查询code和name
+ wrapper.select(SysResource::getResourceCode, SysResource::getResourceName);
+
+ List menuResourceList = this.list(wrapper);
+
+ // 增加返回虚拟菜单的情况
+ SysResource sysResource = new SysResource();
+ sysResource.setResourceCode("");
+ sysResource.setResourceName("虚拟目录(空)");
+ menuResourceList.add(0, sysResource);
+
+ return menuResourceList;
+ }
+
+ @Override
+ public List getRoleResourceTree(Long roleId, Boolean treeBuildFlag, Integer resourceBizType) {
+
+ // 查询当前角色已有的接口
+ List resourceList = roleServiceApi.getRoleResourceList(Collections.singletonList(roleId));
+
+ // 该角色已拥有权限
+ List alreadyList = new ArrayList<>();
+ for (SysRoleResourceDTO sysRoleResponse : resourceList) {
+ alreadyList.add(sysRoleResponse.getResourceCode());
+ }
+
+ return this.getResourceList(alreadyList, treeBuildFlag, resourceBizType);
+ }
+
+ @Override
+ public List getResourceList(List resourceCodes, Boolean treeBuildFlag, Integer resourceBizType) {
+ List res = new ArrayList<>();
+
+ // 获取所有的资源
+ LambdaQueryWrapper sysResourceLambdaQueryWrapper = new LambdaQueryWrapper<>();
+ sysResourceLambdaQueryWrapper.select(SysResource::getAppCode, SysResource::getModularCode, SysResource::getModularName, SysResource::getResourceCode, SysResource::getUrl,
+ SysResource::getResourceName);
+
+ // 只查询需要授权的接口
+ sysResourceLambdaQueryWrapper.eq(SysResource::getRequiredPermissionFlag, YesOrNotEnum.Y.getCode());
+
+ // 查询指定范围的资源
+ sysResourceLambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(resourceBizType), SysResource::getResourceBizType, resourceBizType);
+
+ LoginUserApi loginUserApi = LoginContext.me();
+ if (!loginUserApi.getSuperAdminFlag()) {
+ // 获取权限列表
+ List roleIds = loginUserApi.getLoginUser().getSimpleRoleInfoList().parallelStream().map(SimpleRoleInfo::getRoleId).collect(Collectors.toList());
+ Set resourceCodeList = roleServiceApi.getRoleResourceCodeList(roleIds);
+ if (!resourceCodeList.isEmpty()) {
+ sysResourceLambdaQueryWrapper.in(SysResource::getResourceCode, resourceCodeList);
+ }
+ }
+
+ List allResource = this.list(sysResourceLambdaQueryWrapper);
+
+ // 根据模块名称把资源分类
+ Map> modularMap = new HashMap<>();
+ for (SysResource sysResource : allResource) {
+ List sysResources = modularMap.get(sysResource.getModularName());
+
+ // 没有就新建一个
+ if (ObjectUtil.isEmpty(sysResources)) {
+ sysResources = new ArrayList<>();
+ modularMap.put(sysResource.getModularName(), sysResources);
+ }
+ // 把自己加入进去
+ sysResources.add(sysResource);
+ }
+
+ // 创建一级节点
+ for (Map.Entry> entry : modularMap.entrySet()) {
+ ResourceTreeNode item = new ResourceTreeNode();
+ item.setResourceFlag(false);
+ String id = IdWorker.get32UUID();
+ item.setCode(id);
+ item.setParentCode(RuleConstants.TREE_ROOT_ID.toString());
+ item.setNodeName(entry.getKey());
+
+ // 设置临时变量,统计半开状态
+ int checkedNumber = 0;
+
+ //创建二级节点
+ for (SysResource resource : entry.getValue()) {
+ ResourceTreeNode subItem = new ResourceTreeNode();
+ // 判断是否已经拥有
+ if (!resourceCodes.contains(resource.getResourceCode())) {
+ subItem.setChecked(false);
+ } else {
+ checkedNumber++;
+
+ // 让父类也选择
+ item.setChecked(true);
+ subItem.setChecked(true);
+ }
+ subItem.setResourceFlag(true);
+ subItem.setNodeName(resource.getResourceName());
+ subItem.setCode(resource.getResourceCode());
+ subItem.setParentCode(id);
+ res.add(subItem);
+ }
+
+ // 统计选中的数量
+ if (checkedNumber == 0) {
+ item.setChecked(false);
+ item.setIndeterminate(false);
+ } else if (checkedNumber == entry.getValue().size()) {
+ item.setChecked(true);
+ item.setIndeterminate(false);
+ } else {
+ item.setChecked(false);
+ item.setIndeterminate(true);
+ }
+
+ res.add(item);
+ }
+
+ // 根据map组装资源树
+ if (treeBuildFlag) {
+ return new DefaultTreeBuildFactory().doTreeBuild(res);
+ } else {
+ return res;
+ }
+ }
+
+ @Override
+ public List getApiResourceTree(ResourceRequest resourceRequest) {
+
+ // 1. 获取所有的资源
+ LambdaQueryWrapper sysResourceLambdaQueryWrapper = new LambdaQueryWrapper<>();
+ sysResourceLambdaQueryWrapper.eq(SysResource::getViewFlag, YesOrNotEnum.N.getCode());
+ sysResourceLambdaQueryWrapper.select(SysResource::getAppCode, SysResource::getModularCode, SysResource::getModularName, SysResource::getResourceCode, SysResource::getUrl,
+ SysResource::getResourceName);
+
+ // 查询条件
+ if (ObjectUtil.isNotEmpty(resourceRequest.getResourceName())) {
+ sysResourceLambdaQueryWrapper.like(SysResource::getUrl, resourceRequest.getResourceName()).or().like(SysResource::getResourceName, resourceRequest.getResourceName());
+ }
+
+ List allResource = this.list(sysResourceLambdaQueryWrapper);
+
+ // 2. 按应用和模块编码设置map
+ Map>> appModularResources = divideResources(allResource);
+
+ // 3. 创建模块code和模块name的映射
+ Map modularCodeName = createModularCodeName(allResource);
+
+ // 4. 根据map组装资源树
+ return createResourceTree(appModularResources, modularCodeName);
+ }
+
+ @Override
+ public ResourceDefinition getApiResourceDetail(ResourceRequest resourceRequest) {
+ LambdaQueryWrapper sysResourceLambdaQueryWrapper = new LambdaQueryWrapper<>();
+ sysResourceLambdaQueryWrapper.eq(SysResource::getResourceCode, resourceRequest.getResourceCode());
+ SysResource sysResource = this.getOne(sysResourceLambdaQueryWrapper);
+ if (sysResource != null) {
+
+ // 实体转化为ResourceDefinition
+ ResourceDefinition resourceDefinition = ResourceFactory.createResourceDefinition(sysResource);
+
+ // 填充具体的提示信息
+ return ResourceFactory.fillResourceDetail(resourceDefinition);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void deleteResourceByProjectCode(String projectCode) {
+ LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(SysResource::getProjectCode, projectCode);
+ this.remove(wrapper);
+ }
+
+ @Override
+ public void updateResourceAppCode(String newAppCode) {
+ // 获取所有资源表信息
+ List list = this.list();
+
+ // 批量更新资源编码
+ for (SysResource sysResource : list) {
+ String newResourceCode = GunsResourceCodeUtil.replace(sysResource.getResourceCode(), newAppCode);
+ sysResource.setResourceCode(newResourceCode);
+ }
+
+ this.updateBatchById(list);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void reportResources(@RequestBody ReportResourceParam reportResourceReq) {
+ this.reportResourcesAndGetResult(reportResourceReq);
+ }
+
+ @Override
+ public List reportResourcesAndGetResult(ReportResourceParam reportResourceReq) {
+ String projectCode = reportResourceReq.getProjectCode();
+ Map> resourceDefinitions = reportResourceReq.getResourceDefinitions();
+
+ if (ObjectUtil.isEmpty(projectCode) || resourceDefinitions == null) {
+ return new ArrayList<>();
+ }
+
+ // 根据project删除该项目下的所有资源
+ this.deleteResourceByProjectCode(projectCode);
+
+ // 获取当前应用的所有资源
+ ArrayList allResources = new ArrayList<>();
+ ArrayList resourceDefinitionArrayList = new ArrayList<>();
+ for (Map.Entry> appModularResources : resourceDefinitions.entrySet()) {
+ Map value = appModularResources.getValue();
+ for (Map.Entry modularResources : value.entrySet()) {
+ resourceDefinitionArrayList.add(modularResources.getValue());
+ SysResource resource = ResourceFactory.createResource(modularResources.getValue());
+ allResources.add(resource);
+ }
+ }
+
+ // 将资源存入库中
+ DbTypeEnum currentDbType = DbOperatorContext.me().getCurrentDbType();
+ if (DbTypeEnum.MYSQL.equals(currentDbType)) {
+ // 分批插入记录
+ List> split = ListUtil.split(allResources, RuleConstants.DEFAULT_BATCH_INSERT_SIZE);
+ for (List sysResources : split) {
+ this.getBaseMapper().insertBatchSomeColumn(sysResources);
+ }
+ } else {
+ this.saveBatch(allResources, allResources.size());
+ }
+
+ // 将资源存入缓存一份
+ Map resourceDefinitionMap = ResourceFactory.orderedResourceDefinition(resourceDefinitionArrayList);
+ for (Map.Entry entry : resourceDefinitionMap.entrySet()) {
+ resourceCache.put(entry.getKey(), entry.getValue());
+ }
+
+ // 组装返回结果
+ ArrayList finalResult = new ArrayList<>();
+ for (SysResource item : allResources) {
+ SysResourcePersistencePojo sysResourcePersistencePojo = new SysResourcePersistencePojo();
+ BeanUtil.copyProperties(item, sysResourcePersistencePojo);
+ finalResult.add(sysResourcePersistencePojo);
+ }
+
+ return finalResult;
+ }
+
+ @Override
+ public ResourceDefinition getResourceByUrl(@RequestBody ResourceUrlParam resourceUrlReq) {
+ if (ObjectUtil.isEmpty(resourceUrlReq.getUrl())) {
+ return null;
+ } else {
+
+ // 先从缓存中查询
+ ResourceDefinition tempCachedResourceDefinition = resourceCache.get(resourceUrlReq.getUrl());
+ if (tempCachedResourceDefinition != null) {
+ return tempCachedResourceDefinition;
+ }
+
+ // 缓存中没有去数据库查询
+ List resources = resourceMapper.selectList(new QueryWrapper().eq("url", resourceUrlReq.getUrl()));
+
+ if (resources == null || resources.isEmpty()) {
+ return null;
+ } else {
+
+ // 获取接口资源信息
+ SysResource resource = resources.get(0);
+ ResourceDefinition resourceDefinition = new ResourceDefinition();
+ BeanUtils.copyProperties(resource, resourceDefinition);
+
+ // 获取是否需要登录的标记, 判断是否需要登录,如果是则设置为true,否则为false
+ String requiredLoginFlag = resource.getRequiredLoginFlag();
+ resourceDefinition.setRequiredLoginFlag(YesOrNotEnum.Y.name().equals(requiredLoginFlag));
+
+ // 获取请求权限的标记,判断是否有权限,如果有则设置为true,否则为false
+ String requiredPermissionFlag = resource.getRequiredPermissionFlag();
+ resourceDefinition.setRequiredPermissionFlag(YesOrNotEnum.Y.name().equals(requiredPermissionFlag));
+
+ // 查询结果添加到缓存
+ resourceCache.put(resourceDefinition.getUrl(), resourceDefinition);
+
+ return resourceDefinition;
+ }
+ }
+ }
+
+ @Override
+ public Set getResourceUrlsListByCodes(Set resourceCodes) {
+
+ if (resourceCodes == null || resourceCodes.isEmpty()) {
+ return new HashSet<>();
+ }
+
+ // 拼接in条件
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.in(SysResource::getResourceCode, resourceCodes);
+ queryWrapper.select(SysResource::getUrl);
+
+ // 获取资源详情
+ List list = this.list(queryWrapper);
+ return list.stream().map(SysResource::getUrl).collect(Collectors.toSet());
+ }
+
+ @Override
+ public Integer getResourceCount() {
+ long count = this.count();
+ return Convert.toInt(count);
+ }
+
+ @Override
+ public List getTotalResourceCode(ResBizTypeEnum resBizTypeEnum) {
+
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.select(SysResource::getResourceCode, SysResource::getResourceBizType);
+
+ // 根据资源类型查询
+ queryWrapper.eq(ObjectUtil.isNotEmpty(resBizTypeEnum), SysResource::getResourceBizType, resBizTypeEnum.getCode());
+
+ List list = this.list(queryWrapper);
+
+ ArrayList results = new ArrayList<>();
+ for (SysResource sysResource : list) {
+ SysRoleResourceDTO sysRoleResourceDTO = new SysRoleResourceDTO();
+ sysRoleResourceDTO.setResourceCode(sysResource.getResourceCode());
+ sysRoleResourceDTO.setResourceBizType(sysResource.getResourceBizType());
+ results.add(sysRoleResourceDTO);
+ }
+
+ return results;
+ }
+
+ @Override
+ public Integer getResourceBizTypeByCode(String resCode) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.select(SysResource::getResourceBizType);
+ queryWrapper.eq(SysResource::getResourceCode, resCode);
+ SysResource one = this.getOne(queryWrapper, false);
+ if (one == null) {
+ return ResBizTypeEnum.DEFAULT.getCode();
+ } else {
+ return one.getResourceBizType();
+ }
+ }
+
+ /**
+ * 创建wrapper
+ *
+ * @author fengshuonan
+ * @since 2020/11/6 10:16
+ */
+ private LambdaQueryWrapper createWrapper(ResourceRequest resourceRequest) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+
+ if (ObjectUtil.isEmpty(resourceRequest)) {
+ return queryWrapper;
+ }
+
+ // 根据应用编码查询
+ queryWrapper.eq(ObjectUtil.isNotEmpty(resourceRequest.getAppCode()), SysResource::getAppCode, resourceRequest.getAppCode());
+
+ // 根据资源名称
+ queryWrapper.like(ObjectUtil.isNotEmpty(resourceRequest.getResourceName()), SysResource::getResourceName, resourceRequest.getResourceName());
+
+ // 根据资源url
+ queryWrapper.like(ObjectUtil.isNotEmpty(resourceRequest.getUrl()), SysResource::getUrl, resourceRequest.getUrl());
+
+ return queryWrapper;
+ }
+
+ /**
+ * 划分数据库中的资源,切分成应用和模块分类的集合
+ *
+ * @return 第一个key是应用名称,第二个key是模块名称,值是应用对应的模块对应的资源列表
+ * @author fengshuonan
+ * @since 2020/12/18 15:34
+ */
+ private Map>> divideResources(List sysResources) {
+ HashMap>> appModularResources = new HashMap<>();
+ for (SysResource sysResource : sysResources) {
+
+ // 查询应用下有无资源
+ String appCode = sysResource.getAppCode();
+ Map> modularResource = appModularResources.get(appCode);
+
+ // 该应用下没资源就创建一个map
+ if (modularResource == null) {
+ modularResource = new HashMap<>();
+ }
+
+ // 查询当前资源的模块,有没有在appModularResources存在
+ List resourceTreeNodes = modularResource.get(sysResource.getModularCode());
+ if (resourceTreeNodes == null) {
+ resourceTreeNodes = new ArrayList<>();
+ }
+
+ // 将当前资源放入资源集合
+ LayuiApiResourceTreeNode resourceTreeNode = new LayuiApiResourceTreeNode();
+ resourceTreeNode.setResourceFlag(true);
+ resourceTreeNode.setTitle(sysResource.getResourceName());
+ resourceTreeNode.setId(sysResource.getResourceCode());
+ resourceTreeNode.setParentId(sysResource.getModularCode());
+ resourceTreeNode.setUrl(sysResource.getUrl());
+ resourceTreeNode.setSpread(false);
+ resourceTreeNodes.add(resourceTreeNode);
+
+ modularResource.put(sysResource.getModularCode(), resourceTreeNodes);
+ appModularResources.put(appCode, modularResource);
+ }
+ return appModularResources;
+ }
+
+ /**
+ * 创建模块code和name的映射
+ *
+ * @author fengshuonan
+ * @since 2020/12/21 11:23
+ */
+ private Map createModularCodeName(List resources) {
+ HashMap modularCodeName = new HashMap<>();
+ for (SysResource resource : resources) {
+ modularCodeName.put(resource.getModularCode(), resource.getModularName());
+ }
+ return modularCodeName;
+ }
+
+ /**
+ * 根据归好类的资源,创建资源树
+ *
+ * @author fengshuonan
+ * @since 2020/12/18 15:45
+ */
+ private List createResourceTree(Map>> appModularResources, Map modularCodeName) {
+
+ List finalTree = new ArrayList<>();
+
+ // 按应用遍历应用模块资源集合
+ for (String appName : appModularResources.keySet()) {
+
+ // 创建当前应用节点
+ LayuiApiResourceTreeNode appNode = new LayuiApiResourceTreeNode();
+ appNode.setId(appName);
+ appNode.setTitle(appName);
+ appNode.setSpread(true);
+ appNode.setResourceFlag(false);
+ appNode.setParentId(TreeConstants.DEFAULT_PARENT_ID.toString());
+
+ // 遍历当前应用下的模块资源
+ Map> modularResources = appModularResources.get(appName);
+
+ // 创建模块节点
+ ArrayList modularNodes = new ArrayList<>();
+ for (String modularCode : modularResources.keySet()) {
+ LayuiApiResourceTreeNode modularNode = new LayuiApiResourceTreeNode();
+ modularNode.setId(modularCode);
+ modularNode.setTitle(modularCodeName.get(modularCode));
+ modularNode.setParentId(appName);
+ modularNode.setSpread(false);
+ modularNode.setResourceFlag(false);
+ modularNode.setChildren(modularResources.get(modularCode));
+ modularNodes.add(modularNode);
+ }
+
+ // 当前应用下添加模块的资源
+ appNode.setChildren(modularNodes);
+
+ // 添加到最终结果
+ finalTree.add(appNode);
+ }
+
+ return finalTree;
+ }
+
+}