浏览代码

增加swagger3 支持

zhou-hao 4 年之前
父节点
当前提交
0fc3c5fcb6
共有 28 个文件被更改,包括 667 次插入96 次删除
  1. 1 1
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/AopAuthorizeDefinition.java
  2. 1 1
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopMethodAuthorizeDefinitionCustomizerParser.java
  3. 2 2
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopMethodAuthorizeDefinitionParser.java
  4. 10 18
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/DefaultAopMethodAuthorizeDefinitionParser.java
  5. 12 7
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/AopAuthorizeDefinitionParser.java
  6. 6 5
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java
  7. 13 1
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/EmptyAuthorizeDefinition.java
  8. 4 0
      hsweb-commons/hsweb-commons-api/pom.xml
  9. 3 1
      hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/GenericEntity.java
  10. 160 0
      hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryNoPagingOperation.java
  11. 163 0
      hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryOperation.java
  12. 30 0
      hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryParamEntity.java
  13. 4 0
      hsweb-commons/hsweb-commons-crud/pom.xml
  14. 3 1
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonWebFluxConfiguration.java
  15. 6 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/ResponseMessage.java
  16. 23 1
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/ResponseMessageWrapper.java
  17. 2 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveDeleteController.java
  18. 40 33
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveQueryController.java
  19. 5 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveSaveController.java
  20. 2 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceDeleteController.java
  21. 97 15
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceQueryController.java
  22. 5 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceSaveController.java
  23. 12 3
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveTreeServiceQueryController.java
  24. 6 0
      hsweb-logging/hsweb-access-logging-aop/pom.xml
  25. 12 3
      hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/logging/aop/AopAccessLoggerSupportAutoConfiguration.java
  26. 35 0
      hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/logging/aop/Swagger3AccessLoggerParser.java
  27. 4 4
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/PermissionSynchronization.java
  28. 6 0
      pom.xml

+ 1 - 1
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();
 }

+ 1 - 1
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);
 }

+ 2 - 2
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);
     }
 

+ 10 - 18
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<CacheKey, AuthorizeDefinition> cache = new ConcurrentHashMap<>();
+    private final Map<CacheKey, AuthorizeDefinition> cache = new ConcurrentHashMap<>();
 
     private List<AopMethodAuthorizeDefinitionCustomizerParser> parserCustomizers;
 
-    private static Set<String> excludeMethodName = new HashSet<>(Arrays.asList("toString", "clone", "hashCode", "getClass"));
+    private static final Set<String> excludeMethodName = new HashSet<>(Arrays.asList("toString", "clone", "hashCode", "getClass"));
 
     @Autowired(required = false)
     public void setParserCustomizers(List<AopMethodAuthorizeDefinitionCustomizerParser> 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() {

+ 12 - 7
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<Annotation> methodAnnotation;
+    private final Set<Annotation> methodAnnotation;
 
-    private Set<Annotation> classAnnotation;
+    private final Set<Annotation> classAnnotation;
 
-    private Map<Class<? extends Annotation>, List<Annotation>> classAnnotationGroup;
+    private final Map<Class<? extends Annotation>, List<Annotation>> classAnnotationGroup;
 
-    private Map<Class<? extends Annotation>, List<Annotation>> methodAnnotationGroup;
+    private final Map<Class<? extends Annotation>, List<Annotation>> 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();

+ 6 - 5
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();

+ 13 - 1
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();
+    }
 }

+ 4 - 0
hsweb-commons/hsweb-commons-api/pom.xml

@@ -29,6 +29,10 @@
             <groupId>org.hibernate.javax.persistence</groupId>
             <artifactId>hibernate-jpa-2.1-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>io.swagger.core.v3</groupId>
+            <artifactId>swagger-annotations</artifactId>
+        </dependency>
 
     </dependencies>
 

+ 3 - 1
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<PK> 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) {

+ 160 - 0
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;
+
+}

+ 163 - 0
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;
+
+}

+ 30 - 0
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();
+    }
 }

+ 4 - 0
hsweb-commons/hsweb-commons-crud/pom.xml

@@ -121,6 +121,10 @@
             <version>${project.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>io.swagger.core.v3</groupId>
+            <artifactId>swagger-annotations</artifactId>
+        </dependency>
     </dependencies>
 
 </project>

+ 3 - 1
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){

+ 6 - 0
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<T> 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 <T> ResponseMessage<T> ok() {

+ 23 - 1
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<String> 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;

+ 2 - 0
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<E, K> {
 
     @DeleteMapping("/{id:.+}")
     @DeleteAction
+    @Operation(summary = "根据ID删除")
     default Mono<E> delete(@PathVariable K id) {
         return getRepository()
                 .findById(Mono.just(id))

+ 40 - 33
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<E, K> {
      */
     @GetMapping("/_query/no-paging")
     @QueryAction
-    default Flux<E> query(QueryParamEntity query) {
+    @QueryOperation(summary = "使用GET方式分页动态查询(不返回总数)",
+            description = "此操作不返回分页总数,如果需要获取全部数据,请设置参数paging=false")
+    default Flux<E> query(@Parameter(hidden = true)  QueryParamEntity query) {
         return getRepository()
                 .createQuery()
                 .setParam(query)
@@ -72,28 +77,12 @@ public interface ReactiveQueryController<E, K> {
      */
     @PostMapping("/_query/no-paging")
     @QueryAction
+    @Operation(summary = "使用POST方式分页动态查询(不返回总数)",
+            description = "此操作不返回分页总数,如果需要获取全部数据,请设置参数paging=false")
     default Flux<E> query(@RequestBody Mono<QueryParamEntity> query) {
         return query.flatMapMany(this::query);
     }
 
-    /**
-     * 统计查询
-     *
-     * <pre>
-     *     GET /_count
-     * </pre>
-     *
-     * @param query 查询条件
-     * @return 统计结果
-     */
-    @GetMapping("/_count")
-    @QueryAction
-    default Mono<Integer> count(QueryParamEntity query) {
-        return getRepository()
-                .createQuery()
-                .setParam(query)
-                .count();
-    }
 
     /**
      * GET方式分页查询
@@ -108,7 +97,8 @@ public interface ReactiveQueryController<E, K> {
      */
     @GetMapping("/_query")
     @QueryAction
-    default Mono<PagerResult<E>> queryPager(QueryParamEntity query) {
+    @QueryOperation(summary = "使用GET方式分页动态查询")
+    default Mono<PagerResult<E>> queryPager(@Parameter(hidden = true) QueryParamEntity query) {
         if (query.getTotal() != null) {
             return getRepository()
                     .createQuery()
@@ -117,41 +107,58 @@ public interface ReactiveQueryController<E, K> {
                     .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<PagerResult<E>> queryPager(@RequestBody Mono<QueryParamEntity> query) {
         return query.flatMap(q -> queryPager(q));
     }
 
     @PostMapping("/_count")
     @QueryAction
+    @Operation(summary = "使用POST方式查询总数")
     default Mono<Integer> count(@RequestBody Mono<QueryParamEntity> query) {
         return query.flatMap(this::count);
     }
 
+    /**
+     * 统计查询
+     *
+     * <pre>
+     *     GET /_count
+     * </pre>
+     *
+     * @param query 查询条件
+     * @return 统计结果
+     */
+    @GetMapping("/_count")
+    @QueryAction
+    @QueryOperation(summary = "使用GET方式查询总数")
+    default Mono<Integer> count(@Parameter(hidden = true) QueryParamEntity query) {
+        return getRepository()
+                .createQuery()
+                .setParam(query)
+                .count();
+    }
+
     @GetMapping("/{id:.+}")
     @QueryAction
+    @Operation(summary = "根据ID查询")
     default Mono<E> getById(@PathVariable K id) {
         return getRepository()
                 .findById(Mono.just(id))
                 .switchIfEmpty(Mono.error(NotFoundException::new));
     }
 
-
 }

+ 5 - 0
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<E, K> {
 
     @PatchMapping
     @SaveAction
+    @Operation(summary = "保存数据", description = "如果传入了id,并且对应数据存在,则尝试覆盖,不存在则新增.")
     default Mono<SaveResult> save(@RequestBody Flux<E> payload) {
         return Authentication.currentReactive()
                 .flatMapMany(auth -> payload.map(entity -> applyAuthentication(entity, auth)))
@@ -59,6 +61,7 @@ public interface ReactiveSaveController<E, K> {
 
     @PostMapping("/_batch")
     @SaveAction
+    @Operation(summary = "批量新增数据")
     default Mono<Integer> add(@RequestBody Flux<E> payload) {
 
         return Authentication.currentReactive()
@@ -70,6 +73,7 @@ public interface ReactiveSaveController<E, K> {
 
     @PostMapping
     @SaveAction
+    @Operation(summary = "新增单个数据,并返回新增后的数据.")
     default Mono<E> add(@RequestBody Mono<E> payload) {
         return Authentication.currentReactive()
                 .flatMap(auth -> payload.map(entity -> applyAuthentication(entity, auth)))
@@ -80,6 +84,7 @@ public interface ReactiveSaveController<E, K> {
 
     @PutMapping("/{id}")
     @SaveAction
+    @Operation(summary = "根据ID修改数据")
     default Mono<Boolean> update(@PathVariable K id, @RequestBody Mono<E> payload) {
 
         return Authentication.currentReactive()

+ 2 - 0
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<E, K> {
 
     @DeleteMapping("/{id:.+}")
     @DeleteAction
+    @Operation(summary = "根据ID删除")
     default Mono<E> delete(@PathVariable K id) {
         return getService()
                 .findById(Mono.just(id))

+ 97 - 15
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<E, K> {
     @Authorize(ignore = true)
     ReactiveCrudService<E, K> getService();
 
+    /**
+     * 查询,但是不返回分页结果.
+     *
+     * <pre>
+     *     GET /_query/no-paging?pageIndex=0&pageSize=20&where=name is 张三&orderBy=id desc
+     * </pre>
+     *
+     * @param query 动态查询条件
+     * @return 结果流
+     * @see QueryParamEntity
+     */
     @GetMapping("/_query/no-paging")
     @QueryAction
-    default Flux<E> query(QueryParamEntity query) {
+    @QueryOperation(summary = "使用GET方式分页动态查询(不返回总数)",
+            description = "此操作不返回分页总数,如果需要获取全部数据,请设置参数paging=false")
+    default Flux<E> query(@Parameter(hidden = true) QueryParamEntity query) {
         return getService()
                 .createQuery()
                 .setParam(query)
                 .fetch();
     }
 
+    /**
+     * POST方式查询.不返回分页结果
+     *
+     * <pre>
+     *     POST /_query/no-paging
+     *
+     *     {
+     *         "pageIndex":0,
+     *         "pageSize":20,
+     *         "where":"name like 张%", //放心使用,没有SQL注入
+     *         "orderBy":"id desc",
+     *         "terms":[ //高级条件
+     *             {
+     *                 "column":"name",
+     *                 "termType":"like",
+     *                 "value":"张%"
+     *             }
+     *         ]
+     *     }
+     * </pre>
+     *
+     * @param query 查询条件
+     * @return 结果流
+     * @see QueryParamEntity
+     */
     @PostMapping("/_query/no-paging")
     @QueryAction
+    @Operation(summary = "使用POST方式分页动态查询(不返回总数)",
+            description = "此操作不返回分页总数,如果需要获取全部数据,请设置参数paging=false")
     default Flux<E> query(@RequestBody Mono<QueryParamEntity> query) {
         return query.flatMapMany(this::query);
     }
 
-    @GetMapping("/_count")
-    @QueryAction
-    default Mono<Integer> count(QueryParamEntity query) {
-        return getService()
-                .createQuery()
-                .setParam(query)
-                .count();
-    }
-
+    /**
+     * GET方式分页查询
+     *
+     * <pre>
+     *    GET /_query/no-paging?pageIndex=0&pageSize=20&where=name is 张三&orderBy=id desc
+     * </pre>
+     *
+     * @param query 查询条件
+     * @return 分页查询结果
+     * @see PagerResult
+     */
     @GetMapping("/_query")
     @QueryAction
-    default Mono<PagerResult<E>> queryPager(QueryParamEntity query) {
-        return getService().queryPager(query);
+    @QueryOperation(summary = "使用GET方式分页动态查询")
+    default Mono<PagerResult<E>> 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<PagerResult<E>> queryPager(@RequestBody Mono<QueryParamEntity> query) {
-        return getService().queryPager(query);
+        return query.flatMap(q -> queryPager(q));
     }
 
     @PostMapping("/_count")
     @QueryAction
+    @Operation(summary = "使用POST方式查询总数")
     default Mono<Integer> count(@RequestBody Mono<QueryParamEntity> query) {
         return query.flatMap(this::count);
     }
 
+    /**
+     * 统计查询
+     *
+     * <pre>
+     *     GET /_count
+     * </pre>
+     *
+     * @param query 查询条件
+     * @return 统计结果
+     */
+    @GetMapping("/_count")
+    @QueryAction
+    @QueryOperation(summary = "使用GET方式查询总数")
+    default Mono<Integer> count(@Parameter(hidden = true) QueryParamEntity query) {
+        return getService()
+                .createQuery()
+                .setParam(query)
+                .count();
+    }
+
     @GetMapping("/{id:.+}")
     @QueryAction
+    @Operation(summary = "根据ID查询")
     default Mono<E> getById(@PathVariable K id) {
         return getService()
-                .findById(Mono.just(id))
+                .findById(id)
                 .switchIfEmpty(Mono.error(NotFoundException::new));
     }
 
-
 }

+ 5 - 0
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<E,K>  {
 
     @PatchMapping
     @SaveAction
+    @Operation(summary = "保存数据", description = "如果传入了id,并且对应数据存在,则尝试覆盖,不存在则新增.")
     default Mono<SaveResult> save(@RequestBody Flux<E> payload) {
         return Authentication.currentReactive()
                 .flatMapMany(auth -> payload.map(entity -> applyAuthentication(entity, auth)))
@@ -56,6 +58,7 @@ public interface ReactiveServiceSaveController<E,K>  {
 
     @PostMapping("/_batch")
     @SaveAction
+    @Operation(summary = "批量新增数据")
     default Mono<Integer> add(@RequestBody Flux<E> payload) {
 
         return Authentication.currentReactive()
@@ -67,6 +70,7 @@ public interface ReactiveServiceSaveController<E,K>  {
 
     @PostMapping
     @SaveAction
+    @Operation(summary = "新增单个数据,并返回新增后的数据.")
     default Mono<E> add(@RequestBody Mono<E> payload) {
         return Authentication.currentReactive()
                 .flatMap(auth -> payload.map(entity -> applyAuthentication(entity, auth)))
@@ -77,6 +81,7 @@ public interface ReactiveServiceSaveController<E,K>  {
 
     @PutMapping("/{id}")
     @SaveAction
+    @Operation(summary = "根据ID修改数据")
     default Mono<Boolean> update(@PathVariable K id, @RequestBody Mono<E> payload) {
 
         return Authentication.currentReactive()

+ 12 - 3
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<E extends TreeSortSupportEnt
 
     @GetMapping("/_query/tree")
     @QueryAction
-    default Mono<List<E>> findAllTree(QueryParamEntity paramEntity) {
+    @QueryOperation(summary = "使用GET动态查询并返回树形结构")
+    default Mono<List<E>> findAllTree(@Parameter(hidden = true) QueryParamEntity paramEntity) {
         return getService().queryResultToTree(paramEntity);
     }
 
     @GetMapping("/_query/_children")
     @QueryAction
-    default Flux<E> findAllChildren(QueryParamEntity paramEntity) {
+    @QueryOperation(summary = "使用GET动态查询并返回子节点数据")
+    default Flux<E> findAllChildren(@Parameter(hidden = true) QueryParamEntity paramEntity) {
         return getService().queryIncludeChildren(paramEntity);
     }
 
     @GetMapping("/_query/_children/tree")
     @QueryAction
-    default Mono<List<E>> findAllChildrenTree(QueryParamEntity paramEntity) {
+    @QueryOperation(summary = "使用GET动态查询并返回子节点树形结构数据")
+    default Mono<List<E>> findAllChildrenTree(@Parameter(hidden = true) QueryParamEntity paramEntity) {
         return getService().queryIncludeChildrenTree(paramEntity);
     }
 
     @PostMapping("/_query/tree")
     @QueryAction
+    @Operation(summary = "使用POST动态查询并返回树形结构")
     default Mono<List<E>> findAllTree(Mono<QueryParamEntity> paramEntity) {
         return getService().queryResultToTree(paramEntity);
     }
 
     @PostMapping("/_query/_children")
     @QueryAction
+    @Operation(summary = "使用POST动态查询并返回子节点数据")
     default Flux<E> findAllChildren(Mono<QueryParamEntity> paramEntity) {
         return paramEntity.flatMapMany(param -> getService().queryIncludeChildren(param));
     }
 
     @PostMapping("/_query/_children/tree")
     @QueryAction
+    @Operation(summary = "使用POST动态查询并返回子节点树形结构数据")
     default Mono<List<E>> findAllChildrenTree(Mono<QueryParamEntity> paramEntity) {
         return paramEntity.flatMap(param -> getService().queryIncludeChildrenTree(param));
     }

+ 6 - 0
hsweb-logging/hsweb-access-logging-aop/pom.xml

@@ -65,5 +65,11 @@
             <artifactId>javax.servlet-api</artifactId>
             <scope>provided</scope>
         </dependency>
+
+        <dependency>
+            <groupId>io.swagger.core.v3</groupId>
+            <artifactId>swagger-annotations</artifactId>
+        </dependency>
+
     </dependencies>
 </project>

+ 12 - 3
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();
     }
 }

+ 35 - 0
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, "");
+    }
+}

+ 4 - 4
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<PermissionEntity, String> permissionRepository;
 
-    private MergedAuthorizeDefinition definition = new MergedAuthorizeDefinition();
+    private final MergedAuthorizeDefinition definition = new MergedAuthorizeDefinition();
 
-    private Map<String, List<OptionalField>> entityFieldsMapping = new HashMap<>();
+    private final Map<String, List<OptionalField>> 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<OptionalField> fields = new HashSet<>();
                         ReflectionUtils.doWithFields(entity, field -> {

+ 6 - 0
pom.xml

@@ -382,6 +382,12 @@
                 <version>1.5.10</version>
             </dependency>
 
+            <dependency>
+                <groupId>io.swagger.core.v3</groupId>
+                <artifactId>swagger-annotations</artifactId>
+                <version>2.1.4</version>
+            </dependency>
+
             <dependency>
                 <groupId>commons-beanutils</groupId>
                 <artifactId>commons-beanutils</artifactId>