diff --git a/kernel-s-sys/sys-business-permission/pom.xml b/kernel-s-sys/sys-business-permission/pom.xml index 677c9edd9..4f4a6d9d7 100644 --- a/kernel-s-sys/sys-business-permission/pom.xml +++ b/kernel-s-sys/sys-business-permission/pom.xml @@ -47,6 +47,25 @@ ${roses.version} + + + cn.stylefeng.roses + cache-api + ${roses.version} + + + cn.stylefeng.roses + cache-sdk-memory + ${roses.version} + true + + + cn.stylefeng.roses + cache-sdk-redis + ${roses.version} + true + + diff --git a/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/cache/MemoryResourceCache.java b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/cache/MemoryResourceCache.java new file mode 100644 index 000000000..3b58d6a08 --- /dev/null +++ b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/cache/MemoryResourceCache.java @@ -0,0 +1,56 @@ +/* + * 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.cache; + +import cn.hutool.cache.impl.TimedCache; +import cn.stylefeng.roses.kernel.cache.memory.AbstractMemoryCacheOperator; +import cn.stylefeng.roses.kernel.scanner.api.constants.ScannerConstants; +import cn.stylefeng.roses.kernel.scanner.api.pojo.resource.ResourceDefinition; + + +/** + * 基于内存的资源缓存 + * + * @author fengshuonan + * @since 2021/5/17 16:05 + */ +public class MemoryResourceCache extends AbstractMemoryCacheOperator { + + /** + * TimedCache的key是资源url,value是ResourceDefinition + * + * @author fengshuonan + * @since 2021/5/17 16:06 + */ + public MemoryResourceCache(TimedCache timedCache) { + super(timedCache); + } + + @Override + public String getCommonKeyPrefix() { + return ScannerConstants.RESOURCE_CACHE_KEY; + } + +} diff --git a/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/cache/RedisResourceCache.java b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/cache/RedisResourceCache.java new file mode 100644 index 000000000..720866026 --- /dev/null +++ b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/cache/RedisResourceCache.java @@ -0,0 +1,62 @@ +/* + * 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.cache; + +import cn.stylefeng.roses.kernel.cache.redis.AbstractRedisHashCacheOperator; +import cn.stylefeng.roses.kernel.scanner.api.constants.ScannerConstants; +import cn.stylefeng.roses.kernel.scanner.api.pojo.resource.ResourceDefinition; +import org.springframework.data.redis.core.RedisTemplate; + + +/** + * 基于redis的资源缓存 + * + * @author fengshuonan + * @since 2021/5/17 16:05 + */ +public class RedisResourceCache extends AbstractRedisHashCacheOperator { + + /** + * RedisTemplate的key是资源url,value是ResourceDefinition + * + * @author fengshuonan + * @since 2021/5/17 16:06 + */ + public RedisResourceCache(RedisTemplate redisTemplate) { + super(redisTemplate); + } + + /** + * hash结构的key + * + * @author fengshuonan + * @since 2021/5/17 17:34 + */ + @Override + public String getCommonKeyPrefix() { + return ScannerConstants.RESOURCE_CACHE_KEY; + } + +} diff --git a/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/controller/ResourceController.java b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/controller/ResourceController.java new file mode 100644 index 000000000..1db67b4e4 --- /dev/null +++ b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/controller/ResourceController.java @@ -0,0 +1,102 @@ +/* + * 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.controller; + +import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult; +import cn.stylefeng.roses.kernel.rule.enums.ResBizTypeEnum; +import cn.stylefeng.roses.kernel.rule.pojo.response.ResponseData; +import cn.stylefeng.roses.kernel.rule.pojo.response.SuccessResponseData; +import cn.stylefeng.roses.kernel.scanner.api.annotation.ApiResource; +import cn.stylefeng.roses.kernel.scanner.api.annotation.GetResource; +import cn.stylefeng.roses.kernel.system.api.pojo.resource.ResourceRequest; +import cn.stylefeng.roses.kernel.system.api.pojo.role.request.SysRoleRequest; +import cn.stylefeng.roses.kernel.system.modular.resource.entity.SysResource; +import cn.stylefeng.roses.kernel.system.modular.resource.pojo.ResourceTreeNode; +import cn.stylefeng.roses.kernel.system.modular.resource.service.SysResourceService; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 资源管理控制器 + * + * @author fengshuonan + * @since 2020/11/24 19:47 + */ +@RestController +@ApiResource(name = "资源管理", resBizType = ResBizTypeEnum.SYSTEM) +public class ResourceController { + + @Resource + private SysResourceService sysResourceService; + + /** + * 获取资源列表 + * + * @author fengshuonan + * @since 2020/11/24 19:47 + */ + @GetResource(name = "获取资源列表", path = "/resource/pageList") + public ResponseData> pageList(ResourceRequest resourceRequest) { + PageResult result = this.sysResourceService.findPage(resourceRequest); + return new SuccessResponseData<>(result); + } + + /** + * 获取资源下拉列表(获取菜单资源) + * + * @author fengshuonan + * @since 2020/11/24 19:51 + */ + @GetResource(name = "获取资源下拉列表", path = "/resource/getMenuResourceList") + public ResponseData> getMenuResourceList(ResourceRequest resourceRequest) { + List menuResourceList = this.sysResourceService.findList(resourceRequest); + return new SuccessResponseData<>(menuResourceList); + } + + /** + * Layui版本--获取资源树列表,用于角色分配接口权限 + * + * @author majianguo + * @since 2021/1/9 15:07 + */ + @GetResource(name = "Layui版本--获取资源树列表,用于角色分配接口权限", path = "/resource/getRoleResourceTree") + public List getLateralTree(SysRoleRequest sysRoleRequest) { + return sysResourceService.getRoleResourceTree(sysRoleRequest.getRoleId(), false, null); + } + + /** + * AntdVue版本--获取资源树列表,用于角色分配接口权限 + * + * @author majianguo + * @since 2021/1/9 15:07 + */ + @GetResource(name = "AntdVue版本--获取资源树列表,用于角色分配接口权限", path = "/resource/getRoleResourceTreeAntdv") + public ResponseData> getLateralTreeChildren(SysRoleRequest sysRoleRequest) { + List resourceLateralTree = sysResourceService.getRoleResourceTree(sysRoleRequest.getRoleId(), true, sysRoleRequest.getResourceBizType()); + return new SuccessResponseData<>(resourceLateralTree); + } +} diff --git a/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/controller/ResourceReportController.java b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/controller/ResourceReportController.java new file mode 100644 index 000000000..133bd8aa4 --- /dev/null +++ b/kernel-s-sys/sys-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/resource/controller/ResourceReportController.java @@ -0,0 +1,74 @@ +/* + * 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.controller; + +import cn.stylefeng.roses.kernel.rule.enums.ResBizTypeEnum; +import cn.stylefeng.roses.kernel.scanner.api.ResourceReportApi; +import cn.stylefeng.roses.kernel.scanner.api.annotation.ApiResource; +import cn.stylefeng.roses.kernel.scanner.api.annotation.PostResource; +import cn.stylefeng.roses.kernel.scanner.api.pojo.resource.ReportResourceParam; +import cn.stylefeng.roses.kernel.scanner.api.pojo.resource.SysResourcePersistencePojo; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 微服务汇报资源接口 + * + * @author fengshuonan + * @since 2021/9/25 17:55 + */ +@RestController +@ApiResource(name = "微服务汇报资源接口", resBizType = ResBizTypeEnum.SYSTEM) +public class ResourceReportController { + + @Resource + private ResourceReportApi resourceReportApi; + + /** + * 接收远程服务资源 + * + * @author fengshuonan + * @since 2021/9/25 17:55 + */ + @PostResource(path = "/resourceService/reportResources", name = "汇报资源") + public void reportResources(@RequestBody ReportResourceParam reportResourceReq) { + resourceReportApi.reportResources(reportResourceReq); + } + + /** + * 接收远程服务资源 + * + * @author fengshuonan + * @since 2021/9/25 17:55 + */ + @PostResource(path = "/resourceService/reportResourcesAndGetResult", name = "汇报资源") + public List 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; + } + +}