From 007194fb1e778f9d3e5aab922436fb573bd0712e Mon Sep 17 00:00:00 2001 From: fengshuonan Date: Tue, 5 Jan 2021 15:08:06 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90resource=E3=80=91=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E5=8A=A0=E4=B8=8AviewFlag=E6=A0=87=E8=AF=86?= =?UTF-8?q?=EF=BC=8C=E7=94=A8=E6=9D=A5=E5=8C=BA=E5=88=86=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E5=99=A8=E8=BF=94=E5=9B=9E=E8=A7=86=E5=9B=BE=E8=BF=98=E6=98=AF?= =?UTF-8?q?json=EF=BC=8C=E8=A7=86=E5=9B=BE=E6=8E=A7=E5=88=B6=E5=99=A8?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E4=BB=A5/view=E5=88=A4=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resource/api/annotation/ApiResource.java | 8 +++ .../resource/api/annotation/GetResource.java | 8 +++ .../resource/api/annotation/PostResource.java | 8 +++ .../api/constants/ScannerConstants.java | 5 ++ .../api/pojo/resource/ResourceDefinition.java | 8 +++ .../resource/scanner/ApiResourceScanner.java | 59 +++++++++++++++---- .../resource/modular/entity/SysResource.java | 9 +++ .../modular/factory/ResourceFactory.java | 12 +++- 8 files changed, 103 insertions(+), 14 deletions(-) diff --git a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/annotation/ApiResource.java b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/annotation/ApiResource.java index 8fb26d144..63274c3e3 100644 --- a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/annotation/ApiResource.java +++ b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/annotation/ApiResource.java @@ -58,6 +58,14 @@ public @interface ApiResource { */ boolean requiredPermission() default true; + /** + * 是否是视图类型:true-是,false-否 + * 如果是视图类型,url需要以 '/view' 开头, + * 视图类型的接口会渲染出html界面,而不是json数据, + * 视图层一般会在前后端不分离项目出现 + */ + boolean viewFlag() default false; + /** * 资源的响应类型,用于生成api文档 */ diff --git a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/annotation/GetResource.java b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/annotation/GetResource.java index 9e5703d5a..7c8bd47ef 100644 --- a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/annotation/GetResource.java +++ b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/annotation/GetResource.java @@ -53,6 +53,14 @@ public @interface GetResource { */ boolean requiredPermission() default true; + /** + * 是否是视图类型:true-是,false-否 + * 如果是视图类型,url需要以 '/view' 开头, + * 视图类型的接口会渲染出html界面,而不是json数据, + * 视图层一般会在前后端不分离项目出现 + */ + boolean viewFlag() default false; + /** * 资源的响应类型,用于生成api文档 */ diff --git a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/annotation/PostResource.java b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/annotation/PostResource.java index d9efcd502..603f66825 100644 --- a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/annotation/PostResource.java +++ b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/annotation/PostResource.java @@ -53,6 +53,14 @@ public @interface PostResource { */ boolean requiredPermission() default true; + /** + * 是否是视图类型:true-是,false-否 + * 如果是视图类型,url需要以 '/view' 开头, + * 视图类型的接口会渲染出html界面,而不是json数据, + * 视图层一般会在前后端不分离项目出现 + */ + boolean viewFlag() default false; + /** * 资源的响应类型,用于生成api文档 */ diff --git a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/constants/ScannerConstants.java b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/constants/ScannerConstants.java index 305c5357a..e3f9b6696 100644 --- a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/constants/ScannerConstants.java +++ b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/constants/ScannerConstants.java @@ -28,4 +28,9 @@ public interface ScannerConstants { */ Integer REPORT_RESOURCE_LISTENER_SORT = 200; + /** + * 视图类型的控制器url路径开头 + */ + String VIEW_CONTROLLER_PATH_START_WITH = "/view"; + } diff --git a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/pojo/resource/ResourceDefinition.java b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/pojo/resource/ResourceDefinition.java index 55917d3f7..7298edf13 100644 --- a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/pojo/resource/ResourceDefinition.java +++ b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/resource/api/pojo/resource/ResourceDefinition.java @@ -64,6 +64,14 @@ public class ResourceDefinition implements Serializable { */ private String ipAddress; + /** + * 是否是视图类型:true-是,false-否 + * 如果是视图类型,url需要以 '/view' 开头, + * 视图类型的接口会渲染出html界面,而不是json数据, + * 视图层一般会在前后端不分离项目出现 + */ + private Boolean viewFlag; + /** * 资源的请求路径 */ diff --git a/kernel-d-scanner/scanner-sdk-scanner/src/main/java/cn/stylefeng/roses/kernel/resource/scanner/ApiResourceScanner.java b/kernel-d-scanner/scanner-sdk-scanner/src/main/java/cn/stylefeng/roses/kernel/resource/scanner/ApiResourceScanner.java index b27f19f8e..e69290768 100644 --- a/kernel-d-scanner/scanner-sdk-scanner/src/main/java/cn/stylefeng/roses/kernel/resource/scanner/ApiResourceScanner.java +++ b/kernel-d-scanner/scanner-sdk-scanner/src/main/java/cn/stylefeng/roses/kernel/resource/scanner/ApiResourceScanner.java @@ -7,6 +7,7 @@ import cn.stylefeng.roses.kernel.resource.api.ResourceCollectorApi; import cn.stylefeng.roses.kernel.resource.api.annotation.ApiResource; import cn.stylefeng.roses.kernel.resource.api.annotation.GetResource; import cn.stylefeng.roses.kernel.resource.api.annotation.PostResource; +import cn.stylefeng.roses.kernel.resource.api.constants.ScannerConstants; import cn.stylefeng.roses.kernel.resource.api.exception.ScannerException; import cn.stylefeng.roses.kernel.resource.api.holder.IpAddrHolder; import cn.stylefeng.roses.kernel.resource.api.pojo.resource.ResourceDefinition; @@ -173,11 +174,11 @@ public class ApiResourceScanner implements BeanPostProcessor { * @author fengshuonan * @date 2020/12/9 11:22 */ - private ResourceDefinition createDefinition(Class clazz, Method method, Annotation apiResource) { + private ResourceDefinition createDefinition(Class controllerClass, Method method, Annotation apiResource) { ResourceDefinition resourceDefinition = new ResourceDefinition(); // 填充控制器类的名称 - resourceDefinition.setClassName(clazz.getSimpleName()); + resourceDefinition.setClassName(controllerClass.getSimpleName()); // 填充方法名称 resourceDefinition.setMethodName(method.getName()); @@ -186,14 +187,14 @@ public class ApiResourceScanner implements BeanPostProcessor { String className = resourceDefinition.getClassName(); int controllerIndex = className.indexOf("Controller"); if (controllerIndex == -1) { - String userTip = StrUtil.format(ERROR_CONTROLLER_NAME.getUserTip(), clazz.getName()); + String userTip = StrUtil.format(ERROR_CONTROLLER_NAME.getUserTip(), controllerClass.getName()); throw new ScannerException(ERROR_CONTROLLER_NAME, userTip); } String modular = className.substring(0, controllerIndex); resourceDefinition.setModularCode(modular); // 填充模块的中文名称 - ApiResource classApiAnnotation = clazz.getAnnotation(ApiResource.class); + ApiResource classApiAnnotation = controllerClass.getAnnotation(ApiResource.class); resourceDefinition.setModularName(classApiAnnotation.name()); // 如果控制器类上标识了appCode则应用标识上的appCode,如果控制器上没标识则用配置文件中的appCode @@ -213,16 +214,33 @@ public class ApiResourceScanner implements BeanPostProcessor { // 填充其他属性 String name = invokeAnnotationMethod(apiResource, "name", String.class); - String[] path = invokeAnnotationMethod(apiResource, "path", String[].class); + String[] methodPath = invokeAnnotationMethod(apiResource, "path", String[].class); RequestMethod[] requestMethods = invokeAnnotationMethod(apiResource, "method", RequestMethod[].class); - Boolean menuFlag = invokeAnnotationMethod(apiResource, "menuFlag", Boolean.class); Boolean requiredLogin = invokeAnnotationMethod(apiResource, "requiredLogin", Boolean.class); Boolean requiredPermission = invokeAnnotationMethod(apiResource, "requiredPermission", Boolean.class); + Boolean viewFlag = invokeAnnotationMethod(apiResource, "viewFlag", Boolean.class); resourceDefinition.setRequiredLoginFlag(requiredLogin); resourceDefinition.setRequiredPermissionFlag(requiredPermission); resourceDefinition.setResourceName(name); - resourceDefinition.setUrl(getControllerClassRequestPath(clazz, path[0])); + + // 根据控制器和控制器方法的path组装最后的url + String controllerMethodPath = createControllerPath(controllerClass, methodPath[0]); + resourceDefinition.setUrl(createFinalUrl(controllerMethodPath)); + + // 如果注解标识是视图类型,则判断该资源是视图类型(优先级最高) + if (viewFlag) { + resourceDefinition.setViewFlag(true); + } + // 如果资源url是以/view开头,则是视图类型 + else if (StrUtil.isNotBlank(controllerMethodPath)) { + resourceDefinition.setViewFlag(controllerMethodPath.toLowerCase().startsWith(ScannerConstants.VIEW_CONTROLLER_PATH_START_WITH)); + } + // 其他都是非视图类型 + else { + resourceDefinition.setViewFlag(false); + } + StringBuilder methodNames = new StringBuilder(); for (RequestMethod requestMethod : requestMethods) { methodNames.append(requestMethod.name()).append(","); @@ -272,12 +290,12 @@ public class ApiResourceScanner implements BeanPostProcessor { /** * 根据控制器类上的RequestMapping注解的映射路径,以及方法上的路径,拼出整个接口的路径 * - * @param clazz 控制器类 - * @param path 当前被扫描接口的path路径 + * @param clazz 控制器的类 + * @param path 控制器方法注解上的路径 * @author fengshuonan - * @date 2020/12/14 22:17 + * @date 2021/1/5 14:43 */ - private String getControllerClassRequestPath(Class clazz, String path) { + private String createControllerPath(Class clazz, String path) { String controllerPath; ApiResource controllerRequestMapping = clazz.getDeclaredAnnotation(ApiResource.class); @@ -292,6 +310,23 @@ public class ApiResourceScanner implements BeanPostProcessor { } } + // 控制器上的path要以/开头 + if (!controllerPath.startsWith("/")) { + controllerPath = "/" + controllerPath; + } + + return controllerPath + path; + } + + /** + * 根据appCode和contextPath等,拼出整个接口的路径 + * + * @param controllerMethodPath 控制器和控制器方法的path的组合 + * @author fengshuonan + * @date 2020/12/14 22:17 + */ + private String createFinalUrl(String controllerMethodPath) { + // 拼接最终url的时候,依据如下规则拼接:/appCode/contextPath/xxx // 第一部分是appCode String appCode = ""; @@ -306,7 +341,7 @@ public class ApiResourceScanner implements BeanPostProcessor { } // 依据如下规则拼接:/appCode/contextPath/xxx - String resultPath = appCode + contextPath + controllerPath + path; + String resultPath = appCode + contextPath + controllerMethodPath; // 前缀多个左斜杠替换为一个 resultPath = resultPath.replaceAll("/+", "/"); diff --git a/kernel-s-system/system-business-resource/src/main/java/cn/stylefeng/roses/kernel/resource/modular/entity/SysResource.java b/kernel-s-system/system-business-resource/src/main/java/cn/stylefeng/roses/kernel/resource/modular/entity/SysResource.java index b0345b3cd..1a23b6ea7 100644 --- a/kernel-s-system/system-business-resource/src/main/java/cn/stylefeng/roses/kernel/resource/modular/entity/SysResource.java +++ b/kernel-s-system/system-business-resource/src/main/java/cn/stylefeng/roses/kernel/resource/modular/entity/SysResource.java @@ -78,6 +78,15 @@ public class SysResource extends BaseEntity { @TableField("ip_address") private String ipAddress; + /** + * 是否是视图类型:Y-是,N-否 + * 如果是视图类型,url需要以 '/view' 开头, + * 视图类型的接口会渲染出html界面,而不是json数据, + * 视图层一般会在前后端不分离项目出现 + */ + @TableField("view_flag") + private String viewFlag; + /** * 资源url */ diff --git a/kernel-s-system/system-business-resource/src/main/java/cn/stylefeng/roses/kernel/resource/modular/factory/ResourceFactory.java b/kernel-s-system/system-business-resource/src/main/java/cn/stylefeng/roses/kernel/resource/modular/factory/ResourceFactory.java index f2fec0c80..98fa63643 100644 --- a/kernel-s-system/system-business-resource/src/main/java/cn/stylefeng/roses/kernel/resource/modular/factory/ResourceFactory.java +++ b/kernel-s-system/system-business-resource/src/main/java/cn/stylefeng/roses/kernel/resource/modular/factory/ResourceFactory.java @@ -9,7 +9,6 @@ import cn.stylefeng.roses.kernel.rule.enums.YesOrNotEnum; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature; import com.alibaba.fastjson.serializer.SerializerFeature; -import org.springframework.beans.BeanUtils; import java.util.Set; @@ -29,7 +28,7 @@ public class ResourceFactory { */ public static SysResource createResource(ResourceDefinition resourceDefinition) { SysResource resource = new SysResource(); - BeanUtils.copyProperties(resourceDefinition, resource); + BeanUtil.copyProperties(resourceDefinition, resource, CopyOptions.create().ignoreError()); resource.setResourceCode(resourceDefinition.getResourceCode()); if (resourceDefinition.getRequiredLoginFlag()) { @@ -44,6 +43,12 @@ public class ResourceFactory { 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)); @@ -81,6 +86,9 @@ public class ResourceFactory { // 设置是否需要权限认证标识,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));