diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/AopAuthorizeDefinition.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/AopAuthorizeDefinition.java index 8394c3008..a53ca7ea1 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/AopAuthorizeDefinition.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/AopAuthorizeDefinition.java @@ -7,7 +7,7 @@ import java.lang.reflect.Method; * @since 1.0 */ public interface AopAuthorizeDefinition extends AuthorizeDefinition { - Class getTargetClass(); + Class getTargetClass(); Method getTargetMethod(); } diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopMethodAuthorizeDefinitionCustomizerParser.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopMethodAuthorizeDefinitionCustomizerParser.java index ec440487e..a66dbe254 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopMethodAuthorizeDefinitionCustomizerParser.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopMethodAuthorizeDefinitionCustomizerParser.java @@ -11,5 +11,5 @@ import java.lang.reflect.Method; * @author zhouhao */ public interface AopMethodAuthorizeDefinitionCustomizerParser { - AuthorizeDefinition parse(Class target, Method method, MethodInterceptorContext context); + AuthorizeDefinition parse(Class target, Method method, MethodInterceptorContext context); } diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopMethodAuthorizeDefinitionParser.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopMethodAuthorizeDefinitionParser.java index 82d59682e..3e40edf24 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopMethodAuthorizeDefinitionParser.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopMethodAuthorizeDefinitionParser.java @@ -21,9 +21,9 @@ public interface AopMethodAuthorizeDefinitionParser { * @param method method * @return 权限控制定义, 如果不进行权限控制则返回{@code null} */ - AuthorizeDefinition parse(Class target, Method method, MethodInterceptorContext context); + AuthorizeDefinition parse(Class target, Method method, MethodInterceptorContext context); - default AuthorizeDefinition parse(Class target, Method method) { + default AuthorizeDefinition parse(Class target, Method method) { return parse(target, method, null); } diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/DefaultAopMethodAuthorizeDefinitionParser.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/DefaultAopMethodAuthorizeDefinitionParser.java index a5e9239a6..0a5f9efaf 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/DefaultAopMethodAuthorizeDefinitionParser.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/DefaultAopMethodAuthorizeDefinitionParser.java @@ -1,5 +1,6 @@ package org.hswebframework.web.authorization.basic.aop; +import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; import org.hswebframework.web.aop.MethodInterceptorContext; import org.hswebframework.web.authorization.annotation.Authorize; @@ -29,11 +30,11 @@ import java.util.concurrent.ConcurrentHashMap; @Slf4j public class DefaultAopMethodAuthorizeDefinitionParser implements AopMethodAuthorizeDefinitionParser { - private Map cache = new ConcurrentHashMap<>(); + private final Map cache = new ConcurrentHashMap<>(); private List parserCustomizers; - private static Set excludeMethodName = new HashSet<>(Arrays.asList("toString", "clone", "hashCode", "getClass")); + private static final Set excludeMethodName = new HashSet<>(Arrays.asList("toString", "clone", "hashCode", "getClass")); @Autowired(required = false) public void setParserCustomizers(List parserCustomizers) { @@ -47,7 +48,7 @@ public class DefaultAopMethodAuthorizeDefinitionParser implements AopMethodAutho @Override @SuppressWarnings("all") - public AuthorizeDefinition parse(Class target, Method method, MethodInterceptorContext context) { + public AuthorizeDefinition parse(Class target, Method method, MethodInterceptorContext context) { if (excludeMethodName.contains(method.getName())) { return null; } @@ -87,28 +88,19 @@ public class DefaultAopMethodAuthorizeDefinitionParser implements AopMethodAutho } } - public CacheKey buildCacheKey(Class target, Method method) { + public CacheKey buildCacheKey(Class target, Method method) { return new CacheKey(ClassUtils.getUserClass(target), method); } - class CacheKey { - private Class type; - private Method method; + @EqualsAndHashCode + static class CacheKey { + private final Class type; + private final Method method; - public CacheKey(Class type, Method method) { + public CacheKey(Class type, Method method) { this.type = type; this.method = method; } - - @Override - public int hashCode() { - return Arrays.asList(type, method).hashCode(); - } - - @Override - public boolean equals(Object obj) { - return obj != null && this.hashCode() == obj.hashCode(); - } } public void destroy() { diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/AopAuthorizeDefinitionParser.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/AopAuthorizeDefinitionParser.java index db3aa39f1..e2810245a 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/AopAuthorizeDefinitionParser.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/AopAuthorizeDefinitionParser.java @@ -5,6 +5,7 @@ import org.hswebframework.web.authorization.define.AopAuthorizeDefinition; import org.hswebframework.web.authorization.define.ResourceActionDefinition; import org.hswebframework.web.authorization.define.ResourceDefinition; import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.util.CollectionUtils; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @@ -24,15 +25,15 @@ public class AopAuthorizeDefinitionParser { DataAccessType.class )); - private Set methodAnnotation; + private final Set methodAnnotation; - private Set classAnnotation; + private final Set classAnnotation; - private Map, List> classAnnotationGroup; + private final Map, List> classAnnotationGroup; - private Map, List> methodAnnotationGroup; + private final Map, List> methodAnnotationGroup; - private DefaultBasicAuthorizeDefinition definition; + private final DefaultBasicAuthorizeDefinition definition; AopAuthorizeDefinitionParser(Class targetClass, Method method) { definition = new DefaultBasicAuthorizeDefinition(); @@ -77,7 +78,7 @@ public class AopAuthorizeDefinitionParser { } } - private void initClassDataAccessAnnotation(){ + private void initClassDataAccessAnnotation() { for (Annotation annotation : classAnnotation) { if (annotation instanceof DataAccessType || annotation instanceof DataAccess) { @@ -135,7 +136,11 @@ public class AopAuthorizeDefinitionParser { } } - AopAuthorizeDefinition parse() { + AopAuthorizeDefinition parse() { + //没有任何注解 + if (CollectionUtils.isEmpty(classAnnotation) && CollectionUtils.isEmpty(methodAnnotation)) { + return EmptyAuthorizeDefinition.instance; + } initClassAnnotation(); initClassDataAccessAnnotation(); initMethodAnnotation(); diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java index f18a0538b..36c70e2f1 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java @@ -30,7 +30,6 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition { @JsonIgnore private Class targetClass; - @JsonIgnore private Method targetMethod; @@ -56,7 +55,9 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition { )); public static AopAuthorizeDefinition from(Class targetClass, Method method) { - return new AopAuthorizeDefinitionParser(targetClass,method).parse(); + AopAuthorizeDefinitionParser parser = new AopAuthorizeDefinitionParser(targetClass, method); + + return parser.parse(); } public void putAnnotation(Authorize ann) { @@ -118,7 +119,7 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition { } DataAccessTypeDefinition typeDefinition = new DataAccessTypeDefinition(); for (DataAccessType dataAccessType : ann.type()) { - if(dataAccessType.ignore()){ + if (dataAccessType.ignore()) { continue; } typeDefinition.setId(dataAccessType.id()); @@ -127,7 +128,7 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition { typeDefinition.setConfiguration(dataAccessType.configuration()); typeDefinition.setDescription(String.join("\n", dataAccessType.description())); } - if(StringUtils.isEmpty(typeDefinition.getId())){ + if (StringUtils.isEmpty(typeDefinition.getId())) { return; } definition.getDataAccess() @@ -136,7 +137,7 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition { } public void putAnnotation(ResourceActionDefinition definition, DataAccessType dataAccessType) { - if(dataAccessType.ignore()){ + if (dataAccessType.ignore()) { return; } DataAccessTypeDefinition typeDefinition = new DataAccessTypeDefinition(); diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/EmptyAuthorizeDefinition.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/EmptyAuthorizeDefinition.java index 91a4758cf..d731e4b00 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/EmptyAuthorizeDefinition.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/EmptyAuthorizeDefinition.java @@ -4,11 +4,13 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.hswebframework.web.authorization.define.*; +import java.lang.reflect.Method; + /** * @author zhouhao */ @NoArgsConstructor(access = AccessLevel.PRIVATE) -public class EmptyAuthorizeDefinition implements AuthorizeDefinition { +public class EmptyAuthorizeDefinition implements AopAuthorizeDefinition { public static EmptyAuthorizeDefinition instance = new EmptyAuthorizeDefinition(); @@ -40,4 +42,14 @@ public class EmptyAuthorizeDefinition implements AuthorizeDefinition { public boolean isEmpty() { return true; } + + @Override + public Class getTargetClass() { + throw new UnsupportedOperationException(); + } + + @Override + public Method getTargetMethod() { + throw new UnsupportedOperationException(); + } } diff --git a/hsweb-commons/hsweb-commons-api/pom.xml b/hsweb-commons/hsweb-commons-api/pom.xml index 86c29df08..3b2a40b39 100644 --- a/hsweb-commons/hsweb-commons-api/pom.xml +++ b/hsweb-commons/hsweb-commons-api/pom.xml @@ -29,6 +29,10 @@ org.hibernate.javax.persistence hibernate-jpa-2.1-api + + io.swagger.core.v3 + swagger-annotations + diff --git a/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/GenericEntity.java b/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/GenericEntity.java index dc5a2511f..3cbe38aab 100644 --- a/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/GenericEntity.java +++ b/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/GenericEntity.java @@ -18,6 +18,7 @@ package org.hswebframework.web.api.crud.entity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.Setter; import org.hswebframework.web.bean.ToString; @@ -35,9 +36,10 @@ import javax.persistence.Id; @Setter public class GenericEntity implements Entity { - @Column(length = 64,updatable = false) + @Column(length = 64, updatable = false) @Id @GeneratedValue(generator = "default_id") + @Schema(description = "id") private PK id; public String toString(String... ignoreProperty) { diff --git a/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryNoPagingOperation.java b/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryNoPagingOperation.java new file mode 100644 index 000000000..e52b0fd7b --- /dev/null +++ b/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryNoPagingOperation.java @@ -0,0 +1,160 @@ +package org.hswebframework.web.api.crud.entity; + + +import io.swagger.v3.oas.annotations.ExternalDocumentation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.extensions.Extension; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.servers.Server; +import org.hswebframework.ezorm.core.param.Term; +import org.springframework.core.annotation.AliasFor; + +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.METHOD; + +@Target({METHOD, ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Operation +public @interface QueryNoPagingOperation { + + /** + * The HTTP method for this operation. + * + * @return the HTTP method of this operation + **/ + @AliasFor(annotation = Operation.class) + String method() default ""; + + /** + * Tags can be used for logical grouping of operations by resources or any other qualifier. + * + * @return the list of tags associated with this operation + **/ + @AliasFor(annotation = Operation.class) + String[] tags() default {}; + + /** + * Provides a brief description of this operation. Should be 120 characters or less for proper visibility in Swagger-UI. + * + * @return a summary of this operation + **/ + @AliasFor(annotation = Operation.class) + String summary() default ""; + + /** + * A verbose description of the operation. + * + * @return a description of this operation + **/ + @AliasFor(annotation = Operation.class) + String description() default ""; + + /** + * Request body associated to the operation. + * + * @return a request body. + */ + @AliasFor(annotation = Operation.class) + RequestBody requestBody() default @RequestBody(); + + /** + * Additional external documentation for this operation. + * + * @return additional documentation about this operation + **/ + @AliasFor(annotation = Operation.class) + ExternalDocumentation externalDocs() default @ExternalDocumentation(); + + /** + * The operationId is used by third-party tools to uniquely identify this operation. + * + * @return the ID of this operation + **/ + @AliasFor(annotation = Operation.class) + String operationId() default ""; + + /** + * An optional array of parameters which will be added to any automatically detected parameters in the method itself. + * + * @return the list of parameters for this operation + **/ + @AliasFor(annotation = Operation.class) + Parameter[] parameters() default { + @Parameter(name = "where", description = "条件表达式,和terms参数冲突", example = "id = 1", schema = @Schema(implementation = String.class), in = ParameterIn.QUERY), + @Parameter(name = "orderBy", description = "排序表达式,和sorts参数冲突", example = "id desc", schema = @Schema(implementation = String.class), in = ParameterIn.QUERY), + @Parameter(name = "terms[0].column", description = "指定条件字段", schema = @Schema(implementation = String.class), in = ParameterIn.QUERY), + @Parameter(name = "terms[0].termType", description = "条件类型", schema = @Schema(implementation = String.class), example = "like", in = ParameterIn.QUERY), + @Parameter(name = "terms[0].type", description = "多个条件组合方式", schema = @Schema(implementation = Term.Type.class), in = ParameterIn.QUERY), + @Parameter(name = "terms[0].value", description = "条件值", schema = @Schema(implementation = String.class), in = ParameterIn.QUERY), + @Parameter(name = "sorts[0].name", description = "排序字段", schema = @Schema(implementation = String.class), in = ParameterIn.QUERY), + @Parameter(name = "sorts[0].order", description = "顺序,asc或者desc", schema = @Schema(implementation = String.class), in = ParameterIn.QUERY), + }; + + /** + * The list of possible responses as they are returned from executing this operation. + * + * @return the list of responses for this operation + **/ + @AliasFor(annotation = Operation.class) + ApiResponse[] responses() default {}; + + /** + * Allows an operation to be marked as deprecated. Alternatively use the @Deprecated annotation + * + * @return whether or not this operation is deprecated + **/ + @AliasFor(annotation = Operation.class) + boolean deprecated() default false; + + /** + * A declaration of which security mechanisms can be used for this operation. + * + * @return the array of security requirements for this Operation + */ + @AliasFor(annotation = Operation.class) + SecurityRequirement[] security() default {}; + + /** + * An alternative server array to service this operation. + * + * @return the list of servers hosting this operation + **/ + @AliasFor(annotation = Operation.class) + Server[] servers() default {}; + + /** + * The list of optional extensions + * + * @return an optional array of extensions + */ + @AliasFor(annotation = Operation.class) + Extension[] extensions() default {}; + + /** + * Allows this operation to be marked as hidden + * + * @return whether or not this operation is hidden + */ + @AliasFor(annotation = Operation.class) + boolean hidden() default false; + + /** + * Ignores JsonView annotations while resolving operations and types. + * + * @return whether or not to ignore JsonView annotations + */ + @AliasFor(annotation = Operation.class) + boolean ignoreJsonView() default false; + +} diff --git a/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryOperation.java b/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryOperation.java new file mode 100644 index 000000000..636d4c5b1 --- /dev/null +++ b/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryOperation.java @@ -0,0 +1,163 @@ +package org.hswebframework.web.api.crud.entity; + + +import io.swagger.v3.oas.annotations.ExternalDocumentation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.extensions.Extension; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.servers.Server; +import org.hswebframework.ezorm.core.param.Term; +import org.springframework.core.annotation.AliasFor; + +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.METHOD; + +@Target({METHOD, ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Operation +public @interface QueryOperation { + + /** + * The HTTP method for this operation. + * + * @return the HTTP method of this operation + **/ + @AliasFor(annotation = Operation.class) + String method() default ""; + + /** + * Tags can be used for logical grouping of operations by resources or any other qualifier. + * + * @return the list of tags associated with this operation + **/ + @AliasFor(annotation = Operation.class) + String[] tags() default {}; + + /** + * Provides a brief description of this operation. Should be 120 characters or less for proper visibility in Swagger-UI. + * + * @return a summary of this operation + **/ + @AliasFor(annotation = Operation.class) + String summary() default ""; + + /** + * A verbose description of the operation. + * + * @return a description of this operation + **/ + @AliasFor(annotation = Operation.class) + String description() default ""; + + /** + * Request body associated to the operation. + * + * @return a request body. + */ + @AliasFor(annotation = Operation.class) + RequestBody requestBody() default @RequestBody(); + + /** + * Additional external documentation for this operation. + * + * @return additional documentation about this operation + **/ + @AliasFor(annotation = Operation.class) + ExternalDocumentation externalDocs() default @ExternalDocumentation(); + + /** + * The operationId is used by third-party tools to uniquely identify this operation. + * + * @return the ID of this operation + **/ + @AliasFor(annotation = Operation.class) + String operationId() default ""; + + /** + * An optional array of parameters which will be added to any automatically detected parameters in the method itself. + * + * @return the list of parameters for this operation + **/ + @AliasFor(annotation = Operation.class) + Parameter[] parameters() default { + @Parameter(name = "pageSize", description = "每页数量", schema = @Schema(implementation = Integer.class), in = ParameterIn.QUERY), + @Parameter(name = "pageIndex", description = "页码", schema = @Schema(implementation = Integer.class), in = ParameterIn.QUERY), + @Parameter(name = "total", description = "设置了此值后将不重复执行count查询总数", schema = @Schema(implementation = Integer.class), in = ParameterIn.QUERY), + @Parameter(name = "where", description = "条件表达式,和terms参数冲突", example = "id = 1", schema = @Schema(implementation = String.class), in = ParameterIn.QUERY), + @Parameter(name = "orderBy", description = "排序表达式,和sorts参数冲突", example = "id desc", schema = @Schema(implementation = String.class), in = ParameterIn.QUERY), + @Parameter(name = "terms[0].column", description = "指定条件字段", schema = @Schema(implementation = String.class), in = ParameterIn.QUERY), + @Parameter(name = "terms[0].termType", description = "条件类型", schema = @Schema(implementation = String.class), example = "like", in = ParameterIn.QUERY), + @Parameter(name = "terms[0].type", description = "多个条件组合方式", schema = @Schema(implementation = Term.Type.class), in = ParameterIn.QUERY), + @Parameter(name = "terms[0].value", description = "条件值", schema = @Schema(implementation = String.class), in = ParameterIn.QUERY), + @Parameter(name = "sorts[0].name", description = "排序字段", schema = @Schema(implementation = String.class), in = ParameterIn.QUERY), + @Parameter(name = "sorts[0].order", description = "顺序,asc或者desc", schema = @Schema(implementation = String.class), in = ParameterIn.QUERY), + }; + + /** + * The list of possible responses as they are returned from executing this operation. + * + * @return the list of responses for this operation + **/ + @AliasFor(annotation = Operation.class) + ApiResponse[] responses() default {}; + + /** + * Allows an operation to be marked as deprecated. Alternatively use the @Deprecated annotation + * + * @return whether or not this operation is deprecated + **/ + @AliasFor(annotation = Operation.class) + boolean deprecated() default false; + + /** + * A declaration of which security mechanisms can be used for this operation. + * + * @return the array of security requirements for this Operation + */ + @AliasFor(annotation = Operation.class) + SecurityRequirement[] security() default {}; + + /** + * An alternative server array to service this operation. + * + * @return the list of servers hosting this operation + **/ + @AliasFor(annotation = Operation.class) + Server[] servers() default {}; + + /** + * The list of optional extensions + * + * @return an optional array of extensions + */ + @AliasFor(annotation = Operation.class) + Extension[] extensions() default {}; + + /** + * Allows this operation to be marked as hidden + * + * @return whether or not this operation is hidden + */ + @AliasFor(annotation = Operation.class) + boolean hidden() default false; + + /** + * Ignores JsonView annotations while resolving operations and types. + * + * @return whether or not to ignore JsonView annotations + */ + @AliasFor(annotation = Operation.class) + boolean ignoreJsonView() default false; + +} diff --git a/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryParamEntity.java b/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryParamEntity.java index ed8a4dd67..37abb08ae 100644 --- a/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryParamEntity.java +++ b/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryParamEntity.java @@ -1,5 +1,9 @@ package org.hswebframework.web.api.crud.entity; +import io.swagger.v3.oas.annotations.Hidden; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -33,17 +37,39 @@ public class QueryParamEntity extends QueryParam { private static final long serialVersionUID = 8097500947924037523L; + @Getter + @Schema(description = "where条件表达式,与terms参数冲突.") private String where; @Getter + @Schema(description = "orderBy条件表达式,与sorts参数冲突.") private String orderBy; //总数,设置了此值时,在分页查询的时候将不执行count. @Getter @Setter + @Schema(description = "设置了此值后将不重复执行count查询总数") private Integer total; + @Override + @Hidden + public boolean isForUpdate() { + return super.isForUpdate(); + } + + @Override + @Hidden + public int getThinkPageIndex() { + return super.getThinkPageIndex(); + } + + @Override + @Hidden + public int getPageIndexTmp() { + return super.getPageIndexTmp(); + } + /** * 创建一个空的查询参数实体,该实体无任何参数. * @@ -164,4 +190,8 @@ public class QueryParamEntity extends QueryParam { return this; } + @Override + public QueryParamEntity clone() { + return (QueryParamEntity)super.clone(); + } } diff --git a/hsweb-commons/hsweb-commons-crud/pom.xml b/hsweb-commons/hsweb-commons-crud/pom.xml index a5ab45147..b038f6924 100644 --- a/hsweb-commons/hsweb-commons-crud/pom.xml +++ b/hsweb-commons/hsweb-commons-crud/pom.xml @@ -121,6 +121,10 @@ ${project.version} + + io.swagger.core.v3 + swagger-annotations + \ No newline at end of file diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonWebFluxConfiguration.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonWebFluxConfiguration.java index 5da526c95..af48922dd 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonWebFluxConfiguration.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonWebFluxConfiguration.java @@ -3,6 +3,7 @@ package org.hswebframework.web.crud.web; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.ReactiveAdapterRegistry; @@ -21,7 +22,8 @@ public class CommonWebFluxConfiguration { @Bean - @ConditionalOnProperty(prefix = "hsweb.webflux",name = "response-wrapper",havingValue = "enabled",matchIfMissing = true) + @ConditionalOnProperty(prefix = "hsweb.webflux.response-wrapper",name = "enabled",havingValue = "true",matchIfMissing = true) + @ConfigurationProperties(prefix = "hsweb.webflux.response-wrapper") public ResponseMessageWrapper responseMessageWrapper(ServerCodecConfigurer codecConfigurer, RequestedContentTypeResolver resolver, ReactiveAdapterRegistry registry){ diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/ResponseMessage.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/ResponseMessage.java index 8d5218f2b..4ee9c20f4 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/ResponseMessage.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/ResponseMessage.java @@ -1,6 +1,7 @@ package org.hswebframework.web.crud.web; import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import java.io.Serializable; @@ -15,14 +16,19 @@ public class ResponseMessage implements Serializable { private static final long serialVersionUID = 8992436576262574064L; + @Schema(description = "错误消息提示") protected String message; + @Schema(description = "数据内容") protected T result; + @Schema(description = "状态码") private int status; + @Schema(description = "错误码") protected String code; + @Schema(description = "时间戳(毫秒)") protected Long timestamp = System.currentTimeMillis(); public static ResponseMessage ok() { diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/ResponseMessageWrapper.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/ResponseMessageWrapper.java index a7ead6e69..200ac6e59 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/ResponseMessageWrapper.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/ResponseMessageWrapper.java @@ -1,12 +1,16 @@ package org.hswebframework.web.crud.web; +import lombok.Getter; +import lombok.Setter; import org.springframework.core.MethodParameter; import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.codec.HttpMessageWriter; +import org.springframework.util.CollectionUtils; import org.springframework.util.MimeType; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.method.HandlerMethod; import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.accept.RequestedContentTypeResolver; import org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler; @@ -14,7 +18,9 @@ import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class ResponseMessageWrapper extends ResponseBodyResultHandler { @@ -40,9 +46,24 @@ public class ResponseMessageWrapper extends ResponseBodyResultHandler { return Mono.empty(); } + @Setter + @Getter + private Set excludes = new HashSet<>(); + @Override public boolean supports(HandlerResult result) { - Class gen = result.getReturnType().resolveGeneric(0); + + if (!CollectionUtils.isEmpty(excludes) && result.getHandler() instanceof HandlerMethod) { + HandlerMethod method = (HandlerMethod) result.getHandler(); + + String typeName = method.getMethod().getDeclaringClass().getName() + "." + method.getMethod().getName(); + for (String exclude : excludes) { + if (typeName.startsWith(exclude)) { + return false; + } + } + } + Class gen = result.getReturnType().resolveGeneric(0); boolean isAlreadyResponse = gen == ResponseMessage.class || gen == ResponseEntity.class; @@ -61,6 +82,7 @@ public class ResponseMessageWrapper extends ResponseBodyResultHandler { return false; } } + return isStream && super.supports(result) && !isAlreadyResponse; diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveDeleteController.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveDeleteController.java index 5a7fa0567..9bfa0672c 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveDeleteController.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveDeleteController.java @@ -1,5 +1,6 @@ package org.hswebframework.web.crud.web.reactive; +import io.swagger.v3.oas.annotations.Operation; import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository; import org.hswebframework.web.authorization.annotation.Authorize; import org.hswebframework.web.authorization.annotation.DeleteAction; @@ -14,6 +15,7 @@ public interface ReactiveDeleteController { @DeleteMapping("/{id:.+}") @DeleteAction + @Operation(summary = "根据ID删除") default Mono delete(@PathVariable K id) { return getRepository() .findById(Mono.just(id)) diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveQueryController.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveQueryController.java index 31a8d7f0d..1a7bcbd07 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveQueryController.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveQueryController.java @@ -1,7 +1,10 @@ package org.hswebframework.web.crud.web.reactive; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository; import org.hswebframework.web.api.crud.entity.PagerResult; +import org.hswebframework.web.api.crud.entity.QueryOperation; import org.hswebframework.web.api.crud.entity.QueryParamEntity; import org.hswebframework.web.authorization.annotation.Authorize; import org.hswebframework.web.authorization.annotation.QueryAction; @@ -38,7 +41,9 @@ public interface ReactiveQueryController { */ @GetMapping("/_query/no-paging") @QueryAction - default Flux query(QueryParamEntity query) { + @QueryOperation(summary = "使用GET方式分页动态查询(不返回总数)", + description = "此操作不返回分页总数,如果需要获取全部数据,请设置参数paging=false") + default Flux query(@Parameter(hidden = true) QueryParamEntity query) { return getRepository() .createQuery() .setParam(query) @@ -72,28 +77,12 @@ public interface ReactiveQueryController { */ @PostMapping("/_query/no-paging") @QueryAction + @Operation(summary = "使用POST方式分页动态查询(不返回总数)", + description = "此操作不返回分页总数,如果需要获取全部数据,请设置参数paging=false") default Flux query(@RequestBody Mono query) { return query.flatMapMany(this::query); } - /** - * 统计查询 - * - *
-     *     GET /_count
-     * 
- * - * @param query 查询条件 - * @return 统计结果 - */ - @GetMapping("/_count") - @QueryAction - default Mono count(QueryParamEntity query) { - return getRepository() - .createQuery() - .setParam(query) - .count(); - } /** * GET方式分页查询 @@ -108,7 +97,8 @@ public interface ReactiveQueryController { */ @GetMapping("/_query") @QueryAction - default Mono> queryPager(QueryParamEntity query) { + @QueryOperation(summary = "使用GET方式分页动态查询") + default Mono> queryPager(@Parameter(hidden = true) QueryParamEntity query) { if (query.getTotal() != null) { return getRepository() .createQuery() @@ -117,41 +107,58 @@ public interface ReactiveQueryController { .collectList() .map(list -> PagerResult.of(query.getTotal(), list, query)); } - return getRepository() - .createQuery() - .setParam(query) - .count() - .flatMap(total -> { - if (total == 0) { - return Mono.just(PagerResult.empty()); - } - return query(query.clone().rePaging(total)) - .collectList() - .map(list -> PagerResult.of(total, list, query)); - }); + + return Mono.zip( + getRepository().createQuery().setParam(query).count(), + query(query.clone()).collectList(), + (total, data) -> PagerResult.of(total, data, query) + ); + } @PostMapping("/_query") @QueryAction @SuppressWarnings("all") + @Operation(summary = "使用POST方式分页动态查询") default Mono> queryPager(@RequestBody Mono query) { return query.flatMap(q -> queryPager(q)); } @PostMapping("/_count") @QueryAction + @Operation(summary = "使用POST方式查询总数") default Mono count(@RequestBody Mono query) { return query.flatMap(this::count); } + /** + * 统计查询 + * + *
+     *     GET /_count
+     * 
+ * + * @param query 查询条件 + * @return 统计结果 + */ + @GetMapping("/_count") + @QueryAction + @QueryOperation(summary = "使用GET方式查询总数") + default Mono count(@Parameter(hidden = true) QueryParamEntity query) { + return getRepository() + .createQuery() + .setParam(query) + .count(); + } + @GetMapping("/{id:.+}") @QueryAction + @Operation(summary = "根据ID查询") default Mono getById(@PathVariable K id) { return getRepository() .findById(Mono.just(id)) .switchIfEmpty(Mono.error(NotFoundException::new)); } - } diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveSaveController.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveSaveController.java index bb98dd684..3f1f3bfa6 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveSaveController.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveSaveController.java @@ -1,5 +1,6 @@ package org.hswebframework.web.crud.web.reactive; +import io.swagger.v3.oas.annotations.Operation; import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository; import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult; import org.hswebframework.web.api.crud.entity.RecordCreationEntity; @@ -50,6 +51,7 @@ public interface ReactiveSaveController { @PatchMapping @SaveAction + @Operation(summary = "保存数据", description = "如果传入了id,并且对应数据存在,则尝试覆盖,不存在则新增.") default Mono save(@RequestBody Flux payload) { return Authentication.currentReactive() .flatMapMany(auth -> payload.map(entity -> applyAuthentication(entity, auth))) @@ -59,6 +61,7 @@ public interface ReactiveSaveController { @PostMapping("/_batch") @SaveAction + @Operation(summary = "批量新增数据") default Mono add(@RequestBody Flux payload) { return Authentication.currentReactive() @@ -70,6 +73,7 @@ public interface ReactiveSaveController { @PostMapping @SaveAction + @Operation(summary = "新增单个数据,并返回新增后的数据.") default Mono add(@RequestBody Mono payload) { return Authentication.currentReactive() .flatMap(auth -> payload.map(entity -> applyAuthentication(entity, auth))) @@ -80,6 +84,7 @@ public interface ReactiveSaveController { @PutMapping("/{id}") @SaveAction + @Operation(summary = "根据ID修改数据") default Mono update(@PathVariable K id, @RequestBody Mono payload) { return Authentication.currentReactive() diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceDeleteController.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceDeleteController.java index e3392b2df..7908d9659 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceDeleteController.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceDeleteController.java @@ -1,5 +1,6 @@ package org.hswebframework.web.crud.web.reactive; +import io.swagger.v3.oas.annotations.Operation; import org.hswebframework.web.authorization.annotation.Authorize; import org.hswebframework.web.authorization.annotation.DeleteAction; import org.hswebframework.web.crud.service.ReactiveCrudService; @@ -14,6 +15,7 @@ public interface ReactiveServiceDeleteController { @DeleteMapping("/{id:.+}") @DeleteAction + @Operation(summary = "根据ID删除") default Mono delete(@PathVariable K id) { return getService() .findById(Mono.just(id)) diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceQueryController.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceQueryController.java index f9bea1f62..592499a91 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceQueryController.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceQueryController.java @@ -1,6 +1,9 @@ package org.hswebframework.web.crud.web.reactive; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import org.hswebframework.web.api.crud.entity.PagerResult; +import org.hswebframework.web.api.crud.entity.QueryOperation; import org.hswebframework.web.api.crud.entity.QueryParamEntity; import org.hswebframework.web.authorization.annotation.Authorize; import org.hswebframework.web.authorization.annotation.QueryAction; @@ -19,56 +22,135 @@ public interface ReactiveServiceQueryController { @Authorize(ignore = true) ReactiveCrudService getService(); + /** + * 查询,但是不返回分页结果. + * + *
+     *     GET /_query/no-paging?pageIndex=0&pageSize=20&where=name is 张三&orderBy=id desc
+     * 
+ * + * @param query 动态查询条件 + * @return 结果流 + * @see QueryParamEntity + */ @GetMapping("/_query/no-paging") @QueryAction - default Flux query(QueryParamEntity query) { + @QueryOperation(summary = "使用GET方式分页动态查询(不返回总数)", + description = "此操作不返回分页总数,如果需要获取全部数据,请设置参数paging=false") + default Flux query(@Parameter(hidden = true) QueryParamEntity query) { return getService() .createQuery() .setParam(query) .fetch(); } + /** + * POST方式查询.不返回分页结果 + * + *
+     *     POST /_query/no-paging
+     *
+     *     {
+     *         "pageIndex":0,
+     *         "pageSize":20,
+     *         "where":"name like 张%", //放心使用,没有SQL注入
+     *         "orderBy":"id desc",
+     *         "terms":[ //高级条件
+     *             {
+     *                 "column":"name",
+     *                 "termType":"like",
+     *                 "value":"张%"
+     *             }
+     *         ]
+     *     }
+     * 
+ * + * @param query 查询条件 + * @return 结果流 + * @see QueryParamEntity + */ @PostMapping("/_query/no-paging") @QueryAction + @Operation(summary = "使用POST方式分页动态查询(不返回总数)", + description = "此操作不返回分页总数,如果需要获取全部数据,请设置参数paging=false") default Flux query(@RequestBody Mono query) { return query.flatMapMany(this::query); } + /** + * GET方式分页查询 + * + *
+     *    GET /_query/no-paging?pageIndex=0&pageSize=20&where=name is 张三&orderBy=id desc
+     * 
+ * + * @param query 查询条件 + * @return 分页查询结果 + * @see PagerResult + */ + @GetMapping("/_query") + @QueryAction + @QueryOperation(summary = "使用GET方式分页动态查询") + default Mono> queryPager(@Parameter(hidden = true) QueryParamEntity query) { + if (query.getTotal() != null) { + return getService() + .createQuery() + .setParam(query.rePaging(query.getTotal())) + .fetch() + .collectList() + .map(list -> PagerResult.of(query.getTotal(), list, query)); + } + + return Mono.zip( + getService().createQuery().setParam(query).count(), + getService().createQuery().setParam(query).fetch().collectList(), + (total, data) -> PagerResult.of(total, data, query) + ); + + } + + @PostMapping("/_query") + @QueryAction + @SuppressWarnings("all") + @Operation(summary = "使用POST方式分页动态查询") + default Mono> queryPager(@RequestBody Mono query) { + return query.flatMap(q -> queryPager(q)); + } + + @PostMapping("/_count") + @QueryAction + @Operation(summary = "使用POST方式查询总数") + default Mono count(@RequestBody Mono query) { + return query.flatMap(this::count); + } + + /** + * 统计查询 + * + *
+     *     GET /_count
+     * 
+ * + * @param query 查询条件 + * @return 统计结果 + */ @GetMapping("/_count") @QueryAction - default Mono count(QueryParamEntity query) { + @QueryOperation(summary = "使用GET方式查询总数") + default Mono count(@Parameter(hidden = true) QueryParamEntity query) { return getService() .createQuery() .setParam(query) .count(); } - @GetMapping("/_query") - @QueryAction - default Mono> queryPager(QueryParamEntity query) { - return getService().queryPager(query); - } - - @PostMapping("/_query") - @QueryAction - @SuppressWarnings("all") - default Mono> queryPager(@RequestBody Mono query) { - return getService().queryPager(query); - } - - @PostMapping("/_count") - @QueryAction - default Mono count(@RequestBody Mono query) { - return query.flatMap(this::count); - } - @GetMapping("/{id:.+}") @QueryAction + @Operation(summary = "根据ID查询") default Mono getById(@PathVariable K id) { return getService() - .findById(Mono.just(id)) + .findById(id) .switchIfEmpty(Mono.error(NotFoundException::new)); } - } diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceSaveController.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceSaveController.java index b65dc7655..7a9f5480d 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceSaveController.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceSaveController.java @@ -1,5 +1,6 @@ package org.hswebframework.web.crud.web.reactive; +import io.swagger.v3.oas.annotations.Operation; import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult; import org.hswebframework.web.api.crud.entity.RecordCreationEntity; import org.hswebframework.web.api.crud.entity.RecordModifierEntity; @@ -47,6 +48,7 @@ public interface ReactiveServiceSaveController { @PatchMapping @SaveAction + @Operation(summary = "保存数据", description = "如果传入了id,并且对应数据存在,则尝试覆盖,不存在则新增.") default Mono save(@RequestBody Flux payload) { return Authentication.currentReactive() .flatMapMany(auth -> payload.map(entity -> applyAuthentication(entity, auth))) @@ -56,6 +58,7 @@ public interface ReactiveServiceSaveController { @PostMapping("/_batch") @SaveAction + @Operation(summary = "批量新增数据") default Mono add(@RequestBody Flux payload) { return Authentication.currentReactive() @@ -67,6 +70,7 @@ public interface ReactiveServiceSaveController { @PostMapping @SaveAction + @Operation(summary = "新增单个数据,并返回新增后的数据.") default Mono add(@RequestBody Mono payload) { return Authentication.currentReactive() .flatMap(auth -> payload.map(entity -> applyAuthentication(entity, auth))) @@ -77,6 +81,7 @@ public interface ReactiveServiceSaveController { @PutMapping("/{id}") @SaveAction + @Operation(summary = "根据ID修改数据") default Mono update(@PathVariable K id, @RequestBody Mono payload) { return Authentication.currentReactive() diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveTreeServiceQueryController.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveTreeServiceQueryController.java index 7d9deff37..80fa21ee5 100644 --- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveTreeServiceQueryController.java +++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveTreeServiceQueryController.java @@ -1,5 +1,8 @@ package org.hswebframework.web.crud.web.reactive; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import org.hswebframework.web.api.crud.entity.QueryOperation; import org.hswebframework.web.api.crud.entity.QueryParamEntity; import org.hswebframework.web.api.crud.entity.TreeSortSupportEntity; import org.hswebframework.web.authorization.annotation.Authorize; @@ -19,36 +22,42 @@ public interface ReactiveTreeServiceQueryController> findAllTree(QueryParamEntity paramEntity) { + @QueryOperation(summary = "使用GET动态查询并返回树形结构") + default Mono> findAllTree(@Parameter(hidden = true) QueryParamEntity paramEntity) { return getService().queryResultToTree(paramEntity); } @GetMapping("/_query/_children") @QueryAction - default Flux findAllChildren(QueryParamEntity paramEntity) { + @QueryOperation(summary = "使用GET动态查询并返回子节点数据") + default Flux findAllChildren(@Parameter(hidden = true) QueryParamEntity paramEntity) { return getService().queryIncludeChildren(paramEntity); } @GetMapping("/_query/_children/tree") @QueryAction - default Mono> findAllChildrenTree(QueryParamEntity paramEntity) { + @QueryOperation(summary = "使用GET动态查询并返回子节点树形结构数据") + default Mono> findAllChildrenTree(@Parameter(hidden = true) QueryParamEntity paramEntity) { return getService().queryIncludeChildrenTree(paramEntity); } @PostMapping("/_query/tree") @QueryAction + @Operation(summary = "使用POST动态查询并返回树形结构") default Mono> findAllTree(Mono paramEntity) { return getService().queryResultToTree(paramEntity); } @PostMapping("/_query/_children") @QueryAction + @Operation(summary = "使用POST动态查询并返回子节点数据") default Flux findAllChildren(Mono paramEntity) { return paramEntity.flatMapMany(param -> getService().queryIncludeChildren(param)); } @PostMapping("/_query/_children/tree") @QueryAction + @Operation(summary = "使用POST动态查询并返回子节点树形结构数据") default Mono> findAllChildrenTree(Mono paramEntity) { return paramEntity.flatMap(param -> getService().queryIncludeChildrenTree(param)); } diff --git a/hsweb-logging/hsweb-access-logging-aop/pom.xml b/hsweb-logging/hsweb-access-logging-aop/pom.xml index 47221b492..90d572522 100644 --- a/hsweb-logging/hsweb-access-logging-aop/pom.xml +++ b/hsweb-logging/hsweb-access-logging-aop/pom.xml @@ -65,5 +65,11 @@ javax.servlet-api provided + + + io.swagger.core.v3 + swagger-annotations + + \ No newline at end of file diff --git a/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/logging/aop/AopAccessLoggerSupportAutoConfiguration.java b/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/logging/aop/AopAccessLoggerSupportAutoConfiguration.java index c5e64275c..d48019b9e 100644 --- a/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/logging/aop/AopAccessLoggerSupportAutoConfiguration.java +++ b/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/logging/aop/AopAccessLoggerSupportAutoConfiguration.java @@ -6,6 +6,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; /** * AOP 访问日志记录自动配置 @@ -31,20 +32,28 @@ public class AopAccessLoggerSupportAutoConfiguration { } @Bean - public DefaultAccessLoggerParser defaultAccessLoggerParser(){ + public DefaultAccessLoggerParser defaultAccessLoggerParser() { return new DefaultAccessLoggerParser(); } @Bean @ConditionalOnClass(name = "io.swagger.annotations.Api") - public SwaggerAccessLoggerParser swaggerAccessLoggerParser(){ + @Order(10) + public SwaggerAccessLoggerParser swaggerAccessLoggerParser() { return new SwaggerAccessLoggerParser(); } + @Bean + @ConditionalOnClass(name = "io.swagger.v3.oas.annotations.tags.Tag") + @Order(1) + public Swagger3AccessLoggerParser swagger3AccessLoggerParser() { + return new Swagger3AccessLoggerParser(); + } @Bean @ConditionalOnClass(name = "org.hswebframework.web.authorization.annotation.Resource") - public ResourceAccessLoggerParser resourceAccessLoggerParser(){ + @Order(999) + public ResourceAccessLoggerParser resourceAccessLoggerParser() { return new ResourceAccessLoggerParser(); } } diff --git a/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/logging/aop/Swagger3AccessLoggerParser.java b/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/logging/aop/Swagger3AccessLoggerParser.java new file mode 100644 index 000000000..b9c6a8bd0 --- /dev/null +++ b/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/logging/aop/Swagger3AccessLoggerParser.java @@ -0,0 +1,35 @@ +package org.hswebframework.web.logging.aop; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.hswebframework.web.aop.MethodInterceptorHolder; +import org.hswebframework.web.logging.LoggerDefine; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.util.StringUtils; + +import java.lang.reflect.Method; + +public class Swagger3AccessLoggerParser implements AccessLoggerParser { + @Override + public boolean support(Class clazz, Method method) { + + Tag api = AnnotationUtils.findAnnotation(clazz, Tag.class); + Operation operation = AnnotationUtils.findAnnotation(method, Operation.class); + + return api != null || operation != null; + } + + @Override + public LoggerDefine parse(MethodInterceptorHolder holder) { + Tag api = holder.findAnnotation(Tag.class); + Operation operation = holder.findAnnotation(Operation.class); + String action = ""; + if (api != null) { + action = action.concat(api.name()); + } + if (null != operation) { + action = StringUtils.isEmpty(action) ? operation.summary() : action + "-" + operation.summary(); + } + return new LoggerDefine(action, ""); + } +} diff --git a/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/PermissionSynchronization.java b/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/PermissionSynchronization.java index d7ed82f6d..b530b5bca 100644 --- a/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/PermissionSynchronization.java +++ b/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/PermissionSynchronization.java @@ -34,9 +34,9 @@ public class PermissionSynchronization implements CommandLineRunner { @Autowired private ReactiveRepository permissionRepository; - private MergedAuthorizeDefinition definition = new MergedAuthorizeDefinition(); + private final MergedAuthorizeDefinition definition = new MergedAuthorizeDefinition(); - private Map> entityFieldsMapping = new HashMap<>(); + private final Map> entityFieldsMapping = new HashMap<>(); @EventListener public void handleResourceParseEvent(AuthorizeDefinitionInitializedEvent event) { @@ -50,10 +50,10 @@ public class PermissionSynchronization implements CommandLineRunner { return; } if (authorizeDefinition instanceof AopAuthorizeDefinition) { - Class target = ((AopAuthorizeDefinition) authorizeDefinition).getTargetClass(); + Class target = ((AopAuthorizeDefinition) authorizeDefinition).getTargetClass(); if (ReactiveQueryController.class.isAssignableFrom(target) || ReactiveServiceQueryController.class.isAssignableFrom(target)) { - Class entity = ClassUtils.getGenericType(target); + Class entity = ClassUtils.getGenericType(target); if (Entity.class.isAssignableFrom(entity)) { Set fields = new HashSet<>(); ReflectionUtils.doWithFields(entity, field -> { diff --git a/pom.xml b/pom.xml index 90f2154a7..06c1222d5 100644 --- a/pom.xml +++ b/pom.xml @@ -382,6 +382,12 @@ 1.5.10 + + io.swagger.core.v3 + swagger-annotations + 2.1.4 + + commons-beanutils commons-beanutils