From a218dd046050b473cdb7559466848d29fe84d940 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sat, 23 Sep 2023 20:01:50 +0800 Subject: [PATCH 001/101] add 2.7.1 to springboot2.x --- pom.xml | 11 +++++----- springboot-starter-data-fast/pom.xml | 4 ++-- .../fast/DataFastConfiguration.java | 2 +- .../springboot/fast/executor/JpaExecutor.java | 2 +- .../springboot/fast/executor/JpaQuery.java | 6 +++--- .../fast/manager/EntityManagerContent.java | 3 ++- .../manager/EntityManagerInitializer.java | 3 ++- .../springboot/fast/entity/Demo.java | 3 ++- springboot-starter-id-generator/pom.xml | 4 ++-- .../springboot/generator/dao/IdKeyDao.java | 4 ++-- springboot-starter-security-jwt/pom.xml | 4 ++-- .../security/AutoConfiguration.java | 4 ++-- .../configurer/WebSecurityConfigurer.java | 2 +- .../filter/MyAccessDeniedHandler.java | 6 +++--- .../filter/MyAuthenticationFilter.java | 9 ++++---- .../security/filter/MyLoginFilter.java | 8 +++---- .../security/filter/MyLogoutHandler.java | 5 +++-- .../filter/MyLogoutSuccessHandler.java | 6 +++--- .../filter/MyUnAuthenticationEntryPoint.java | 7 ++++--- .../security/filter/SecurityLoginHandler.java | 5 +++-- springboot-starter/pom.xml | 8 +++---- .../framework/dto/request/PageRequest.java | 2 +- .../framework/rest/param/RestParam.java | 6 ++++-- ...HandlerExceptionResolverConfiguration.java | 5 +++-- .../utils/TrustAnyHttpClientFactory.java | 21 ++++++++++--------- .../framework/query/entity/Demo.java | 3 ++- 26 files changed, 76 insertions(+), 67 deletions(-) diff --git a/pom.xml b/pom.xml index 3ba9ed9b..49600c4f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,25 +6,25 @@ org.springframework.boot spring-boot-starter-parent - 3.1.2 + 2.7.16 com.codingapi.springboot springboot-parent - 2.1.11 + 2.7.1 https://github.com/codingapi/springboot-framewrok springboot-parent springboot-parent project for Spring Boot - 17 + 8 yyyy-MM-dd HH:mm:ss - 17 - 17 + 8 + 8 3.0.1 3.11.0 2.10.3 @@ -39,7 +39,6 @@ 0.9.16 1.70 1.2.0 - 2.0 diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 141c3862..d06408f0 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,14 +5,14 @@ springboot-parent com.codingapi.springboot - 2.1.11 + 2.7.1 4.0.0 springboot-starter-data-fast - 17 + 8 diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/DataFastConfiguration.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/DataFastConfiguration.java index 0dcf4268..f977da31 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/DataFastConfiguration.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/DataFastConfiguration.java @@ -12,7 +12,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; -import jakarta.persistence.EntityManager; +import javax.persistence.EntityManager; import java.util.List; @Configuration diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/JpaExecutor.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/JpaExecutor.java index 04067091..a7795989 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/JpaExecutor.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/JpaExecutor.java @@ -5,7 +5,7 @@ import lombok.AllArgsConstructor; import org.springframework.data.domain.Page; -import jakarta.persistence.EntityManager; +import javax.persistence.EntityManager; import java.util.Collection; import java.util.List; diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/JpaQuery.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/JpaQuery.java index dbc4e118..0aa880f0 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/JpaQuery.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/JpaQuery.java @@ -7,9 +7,9 @@ import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; -import jakarta.persistence.EntityManager; -import jakarta.persistence.Parameter; -import jakarta.persistence.Query; +import javax.persistence.EntityManager; +import javax.persistence.Parameter; +import javax.persistence.Query; import java.lang.reflect.Field; import java.util.Set; diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/manager/EntityManagerContent.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/manager/EntityManagerContent.java index 3d7d5c59..96ab6ba2 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/manager/EntityManagerContent.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/manager/EntityManagerContent.java @@ -1,8 +1,9 @@ package com.codingapi.springboot.fast.manager; -import jakarta.persistence.EntityManager; import lombok.Getter; +import javax.persistence.EntityManager; + public class EntityManagerContent { @Getter diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/manager/EntityManagerInitializer.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/manager/EntityManagerInitializer.java index ea87bea4..a262fc20 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/manager/EntityManagerInitializer.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/manager/EntityManagerInitializer.java @@ -1,9 +1,10 @@ package com.codingapi.springboot.fast.manager; -import jakarta.persistence.EntityManager; import lombok.AllArgsConstructor; import org.springframework.beans.factory.InitializingBean; +import javax.persistence.EntityManager; + @AllArgsConstructor public class EntityManagerInitializer implements InitializingBean { diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Demo.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Demo.java index 3e9e66d8..a39afa5d 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Demo.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Demo.java @@ -3,7 +3,8 @@ import lombok.Getter; import lombok.Setter; -import jakarta.persistence.*; +import javax.persistence.*; + @Setter @Getter diff --git a/springboot-starter-id-generator/pom.xml b/springboot-starter-id-generator/pom.xml index c4adbf85..96cf9c71 100644 --- a/springboot-starter-id-generator/pom.xml +++ b/springboot-starter-id-generator/pom.xml @@ -5,14 +5,14 @@ springboot-parent com.codingapi.springboot - 2.1.11 + 2.7.1 4.0.0 springboot-starter-id-generator - 17 + 8 diff --git a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/dao/IdKeyDao.java b/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/dao/IdKeyDao.java index 59332bc0..541a68a5 100644 --- a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/dao/IdKeyDao.java +++ b/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/dao/IdKeyDao.java @@ -62,7 +62,7 @@ public List query(Connection connection, QueryRunner queryRunner) throws @SneakyThrows public IdKey updateMaxId(IdKey generator) { - return dbHelper.updateAndQuery(new DbHelper.IUpdateAndQuery<>() { + return dbHelper.updateAndQuery(new DbHelper.IUpdateAndQuery>() { @Override public List updateAndQuery(Connection connection, QueryRunner queryRunner) throws SQLException { queryRunner.update(connection, "UPDATE ID_GENERATOR SET ID = ID + 1 WHERE TAG = ?", generator.getKey()); @@ -74,7 +74,7 @@ public List updateAndQuery(Connection connection, QueryRunner queryRunner @SneakyThrows public List findAll() throws SQLException { - return dbHelper.query(new DbHelper.IQuery<>() { + return dbHelper.query(new DbHelper.IQuery>() { @Override public List query(Connection connection, QueryRunner queryRunner) throws SQLException { return queryRunner.query(connection, "SELECT * FROM ID_GENERATOR", handler); diff --git a/springboot-starter-security-jwt/pom.xml b/springboot-starter-security-jwt/pom.xml index 6369c486..f3ed9bd4 100644 --- a/springboot-starter-security-jwt/pom.xml +++ b/springboot-starter-security-jwt/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.1.11 + 2.7.1 springboot-starter-security-jwt @@ -15,7 +15,7 @@ springboot-starter-security-jwt project for Spring Boot - 17 + 8 diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java index ba56c691..7587aaa0 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java +++ b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java @@ -82,8 +82,8 @@ public SecurityFilterChain filterChain(HttpSecurity security, Jwt jwt,SecurityLo .and() .authorizeHttpRequests( registry -> { - registry.requestMatchers(properties.getIgnoreUrls()).permitAll() - .requestMatchers(properties.getAuthenticatedUrls()).authenticated() + registry.antMatchers(properties.getIgnoreUrls()).permitAll() + .antMatchers(properties.getAuthenticatedUrls()).authenticated() .anyRequest().permitAll(); } ) diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/configurer/WebSecurityConfigurer.java b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/configurer/WebSecurityConfigurer.java index 7e5f3e75..5bd1ebd5 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/configurer/WebSecurityConfigurer.java +++ b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/configurer/WebSecurityConfigurer.java @@ -15,7 +15,7 @@ public class WebSecurityConfigurer implements WebSecurityCustomizer { @Override public void customize(WebSecurity web) { //ignoring security filters request url - web.ignoring().requestMatchers(securityJwtProperties.getIgnoreUrls()); + web.ignoring().antMatchers(securityJwtProperties.getIgnoreUrls()); } } diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyAccessDeniedHandler.java b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyAccessDeniedHandler.java index 191d1dfc..4387a65b 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyAccessDeniedHandler.java +++ b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyAccessDeniedHandler.java @@ -7,9 +7,9 @@ import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java index 09562919..aaa348a7 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java +++ b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java @@ -6,10 +6,6 @@ import com.codingapi.springboot.security.jwt.Jwt; import com.codingapi.springboot.security.jwt.Token; import com.codingapi.springboot.security.properties.SecurityJwtProperties; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.springframework.security.authentication.AuthenticationManager; @@ -17,8 +13,11 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.util.AntPathMatcher; import org.springframework.util.StringUtils; -import org.springframework.web.filter.OncePerRequestFilter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java index f472ed99..bbfc55af 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java +++ b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java @@ -22,10 +22,10 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.stream.Collectors; diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLogoutHandler.java b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLogoutHandler.java index a27c9d76..b2cbe8d2 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLogoutHandler.java +++ b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLogoutHandler.java @@ -4,8 +4,9 @@ import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutHandler; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + @Slf4j public class MyLogoutHandler implements LogoutHandler { diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLogoutSuccessHandler.java b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLogoutSuccessHandler.java index 16a86c68..7ed35833 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLogoutSuccessHandler.java +++ b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLogoutSuccessHandler.java @@ -7,9 +7,9 @@ import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyUnAuthenticationEntryPoint.java b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyUnAuthenticationEntryPoint.java index ecc91b95..c1e610f8 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyUnAuthenticationEntryPoint.java +++ b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyUnAuthenticationEntryPoint.java @@ -7,9 +7,10 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java index 5ad14115..b8ce2a2a 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java +++ b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java @@ -2,8 +2,9 @@ import com.codingapi.springboot.security.dto.request.LoginRequest; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + public interface SecurityLoginHandler { diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 30709242..8a71dc0a 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,14 +5,14 @@ com.codingapi.springboot springboot-parent - 2.1.11 + 2.7.1 springboot-starter springboot-starter springboot-starter project for Spring Boot - 17 + 8 @@ -47,8 +47,8 @@ - org.apache.httpcomponents.client5 - httpclient5 + org.apache.httpcomponents + httpclient diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java index b7af6da8..77f99ae0 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java @@ -1,6 +1,5 @@ package com.codingapi.springboot.framework.dto.request; -import jakarta.servlet.http.HttpServletRequest; import lombok.Getter; import org.springframework.beans.BeanUtils; import org.springframework.data.domain.Example; @@ -10,6 +9,7 @@ import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import javax.servlet.http.HttpServletRequest; import java.beans.PropertyDescriptor; import java.util.Enumeration; import java.util.HashMap; diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/rest/param/RestParam.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/rest/param/RestParam.java index 51c08a8b..a19efe09 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/rest/param/RestParam.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/rest/param/RestParam.java @@ -41,9 +41,11 @@ private static void fetch(JSONObject object, RestParam builder) { fetch(jsonObject, builder); } - if (value instanceof JSONArray jsonArray) { + if (value instanceof JSONArray) { + JSONArray jsonArray = (JSONArray) value; for (Object o : jsonArray) { - if (o instanceof JSONObject jsonObject) { + if (o instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) o; fetch(jsonObject, builder); } } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/servlet/BasicHandlerExceptionResolverConfiguration.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/servlet/BasicHandlerExceptionResolverConfiguration.java index 573de914..8cf34dc3 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/servlet/BasicHandlerExceptionResolverConfiguration.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/servlet/BasicHandlerExceptionResolverConfiguration.java @@ -2,8 +2,6 @@ import com.codingapi.springboot.framework.exception.LocaleMessageException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; @@ -12,6 +10,9 @@ import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.view.json.MappingJackson2JsonView; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + @Configuration @ConditionalOnClass(name = "org.springframework.web.servlet.HandlerExceptionResolver") public class BasicHandlerExceptionResolverConfiguration { diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/utils/TrustAnyHttpClientFactory.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/utils/TrustAnyHttpClientFactory.java index 25810885..2bc34c3d 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/utils/TrustAnyHttpClientFactory.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/utils/TrustAnyHttpClientFactory.java @@ -2,16 +2,17 @@ import lombok.SneakyThrows; -import org.apache.hc.client5.http.classic.HttpClient; -import org.apache.hc.client5.http.config.RequestConfig; -import org.apache.hc.client5.http.impl.classic.HttpClients; -import org.apache.hc.client5.http.impl.io.BasicHttpClientConnectionManager; -import org.apache.hc.client5.http.socket.ConnectionSocketFactory; -import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory; -import org.apache.hc.client5.http.ssl.NoopHostnameVerifier; -import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; -import org.apache.hc.core5.http.config.Registry; -import org.apache.hc.core5.http.config.RegistryBuilder; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.BasicHttpClientConnectionManager; + import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; diff --git a/springboot-starter/src/test/java/com/codingapi/springboot/framework/query/entity/Demo.java b/springboot-starter/src/test/java/com/codingapi/springboot/framework/query/entity/Demo.java index 664f815a..148a34ae 100644 --- a/springboot-starter/src/test/java/com/codingapi/springboot/framework/query/entity/Demo.java +++ b/springboot-starter/src/test/java/com/codingapi/springboot/framework/query/entity/Demo.java @@ -1,9 +1,10 @@ package com.codingapi.springboot.framework.query.entity; -import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; +import javax.persistence.*; + @Setter @Getter @Table(name = "t_demo") From 76b7b8dffedacdc4be1c5536eab547464945c9ae Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Tue, 26 Sep 2023 10:30:41 +0800 Subject: [PATCH 002/101] fix getExample() 2.7.2 --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- .../springboot/fast/query/FastRepository.java | 34 +++- .../springboot/fast/query/QueryRequest.java | 147 ++++++++++++++++++ springboot-starter-id-generator/pom.xml | 2 +- springboot-starter-security-jwt/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../framework/dto/request/PageRequest.java | 144 ++++++++++++----- .../framework/rest/param/RestParam.java | 5 +- .../query/test/DemoRepositoryTest.java | 6 +- 10 files changed, 294 insertions(+), 52 deletions(-) create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java diff --git a/pom.xml b/pom.xml index 49600c4f..c03da97d 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.7.1 + 2.7.2 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index d06408f0..85efb421 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.1 + 2.7.2 4.0.0 diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/FastRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/FastRepository.java index 71eeb9ae..937b5928 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/FastRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/FastRepository.java @@ -1,21 +1,27 @@ package com.codingapi.springboot.fast.query; import com.codingapi.springboot.framework.dto.request.PageRequest; +import javax.persistence.criteria.Order; +import javax.persistence.criteria.Predicate; import org.springframework.core.ResolvableType; import org.springframework.data.domain.Page; +import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.NoRepositoryBean; +import java.util.List; + @NoRepositoryBean -public interface FastRepository extends JpaRepository, JpaSpecificationExecutor { +public interface FastRepository extends JpaRepository, JpaSpecificationExecutor { - default Page findAll(PageRequest request){ - if(request.hasFilter()){ + default Page findAll(PageRequest request) { + if (request.hasFilter()) { Class clazz = getDomainClass(); - return findAll(request.getExample(clazz),request); + QueryRequest queryRequest = new QueryRequest(request, clazz); + return findAll(queryRequest.getExample(), request); } - return findAll((org.springframework.data.domain.PageRequest)request); + return findAll((org.springframework.data.domain.PageRequest) request); } @@ -25,4 +31,20 @@ default Class getDomainClass() { return (Class) resolvableType.getGeneric(0).resolve(); } -} + + default Page findAllByRequest(PageRequest request) { + if (request.hasFilter()) { + Class clazz = getDomainClass(); + Specification specification = (root, query, criteriaBuilder) -> { + QueryRequest queryRequest = new QueryRequest(request, clazz); + List predicates = queryRequest.getPredicate(root, criteriaBuilder); + List orderList = queryRequest.getOrder(root, criteriaBuilder); + return query.where(predicates.toArray(new Predicate[0])).orderBy(orderList).getRestriction(); + }; + + return findAll(specification, request); + } + return findAll((org.springframework.data.domain.PageRequest) request); + } + +} \ No newline at end of file diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java new file mode 100644 index 00000000..32056ce4 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java @@ -0,0 +1,147 @@ +package com.codingapi.springboot.fast.query; + +import com.codingapi.springboot.framework.dto.request.PageRequest; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.Order; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import org.springframework.beans.BeanUtils; +import org.springframework.data.domain.Example; + +import java.beans.PropertyDescriptor; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class QueryRequest { + + private final PageRequest request; + private final Class clazz; + + public QueryRequest(PageRequest request, Class clazz) { + this.request = request; + this.clazz = clazz; + } + + public Example getExample() { + if (!request.hasFilter()) { + return null; + } + Object entity = null; + try { + entity = clazz.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(clazz); + for (PropertyDescriptor descriptor : descriptors) { + String name = descriptor.getName(); + PageRequest.Filter value = request.getFilters().get(name); + if (value != null) { + try { + descriptor.getWriteMethod().invoke(entity, value.getFilterValue(descriptor.getPropertyType())); + } catch (Exception e) { + } + } + } + return (Example) Example.of(entity); + } + + + public List getOrder(Root root, CriteriaBuilder criteriaBuilder) { + List orderList = new ArrayList<>(); + request.getSort().forEach(sort -> { + if (sort.getDirection().isAscending()) { + orderList.add(criteriaBuilder.asc(root.get(sort.getProperty()))); + } else { + orderList.add(criteriaBuilder.asc(root.get(sort.getProperty()))); + } + }); + return orderList; + } + + public List getPredicate(Root root, CriteriaBuilder criteriaBuilder) { + List predicates = new ArrayList<>(); + for (String key : request.getFilters().keySet()) { + PageRequest.Filter filter = request.getFilters().get(key); + if (filter.isEqual()) { + predicates.add(criteriaBuilder.equal(root.get(key), filter.getValue()[0])); + } + + if (filter.isLike()) { + String matchValue = (String) filter.getValue()[0]; + predicates.add(criteriaBuilder.like(root.get(key), "%" + matchValue + "%")); + } + + if (filter.isBetween()) { + Object value1 = filter.getValue()[0]; + Object value2 = filter.getValue()[2]; + if (value1 instanceof Integer && value2 instanceof Integer) { + predicates.add(criteriaBuilder.between(root.get(key), (Integer) value1, (Integer) value2)); + } + + if (value1 instanceof Long && value2 instanceof Long) { + predicates.add(criteriaBuilder.between(root.get(key), (Long) value1, (Long) value2)); + } + + if (value1 instanceof Date && value2 instanceof Date) { + predicates.add(criteriaBuilder.between(root.get(key), (Date) value1, (Date) value2)); + } + } + + if (filter.isGreaterThan()) { + Object value = filter.getValue()[0]; + if (value instanceof Integer) { + predicates.add(criteriaBuilder.greaterThan(root.get(key), (Integer) value)); + } + if (value instanceof Long) { + predicates.add(criteriaBuilder.greaterThan(root.get(key), (Long) value)); + } + if (value instanceof Date) { + predicates.add(criteriaBuilder.greaterThan(root.get(key), (Date) value)); + } + } + + if (filter.isGreaterThanEqual()) { + Object value = filter.getValue()[0]; + if (value instanceof Integer) { + predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Integer) value)); + } + if (value instanceof Long) { + predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Long) value)); + } + if (value instanceof Date) { + predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Date) value)); + } + } + + if (filter.isLessThan()) { + Object value = filter.getValue()[0]; + if (value instanceof Integer) { + predicates.add(criteriaBuilder.lessThan(root.get(key), (Integer) value)); + } + if (value instanceof Long) { + predicates.add(criteriaBuilder.lessThan(root.get(key), (Long) value)); + } + if (value instanceof Date) { + predicates.add(criteriaBuilder.lessThan(root.get(key), (Date) value)); + } + } + + if (filter.isLessThanEqual()) { + Object value = filter.getValue()[0]; + if (value instanceof Integer) { + predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get(key), (Integer) value)); + } + if (value instanceof Long) { + predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get(key), (Long) value)); + } + if (value instanceof Date) { + predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get(key), (Date) value)); + } + } + } + + return predicates; + } +} \ No newline at end of file diff --git a/springboot-starter-id-generator/pom.xml b/springboot-starter-id-generator/pom.xml index 96cf9c71..3f3a6a34 100644 --- a/springboot-starter-id-generator/pom.xml +++ b/springboot-starter-id-generator/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.1 + 2.7.2 4.0.0 diff --git a/springboot-starter-security-jwt/pom.xml b/springboot-starter-security-jwt/pom.xml index f3ed9bd4..68c74e24 100644 --- a/springboot-starter-security-jwt/pom.xml +++ b/springboot-starter-security-jwt/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.7.1 + 2.7.2 springboot-starter-security-jwt diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 8a71dc0a..74016e37 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.7.1 + 2.7.2 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java index 77f99ae0..42e71cef 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java @@ -1,16 +1,14 @@ package com.codingapi.springboot.framework.dto.request; +import javax.servlet.http.HttpServletRequest; import lombok.Getter; -import org.springframework.beans.BeanUtils; -import org.springframework.data.domain.Example; +import lombok.Setter; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.util.StringUtils; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; -import javax.servlet.http.HttpServletRequest; -import java.beans.PropertyDescriptor; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; @@ -22,7 +20,8 @@ public class PageRequest extends org.springframework.data.domain.PageRequest { private int current; private int pageSize; - private final Map filters = new HashMap<>(); + @Getter + private final Map filters = new HashMap<>(); @Getter private HttpServletRequest servletRequest; @@ -50,7 +49,7 @@ private void syncParameter() { String key = enumeration.nextElement(); String value = servletRequest.getParameter(key); if (StringUtils.hasText(value)) { - this.filters.put(key, value); + addFilter(key, value); } } } @@ -81,26 +80,41 @@ public int getIntParameter(String key, int defaultValue) { return result == null ? defaultValue : Integer.parseInt(result); } - public Map getFilters() { - return filters; - } public String getStringFilter(String key) { - return (String) filters.get(key); + Filter filter = (Filter) filters.get(key); + if (filter != null) { + return (String) filter.getValue()[0]; + } + return null; } public String getStringFilter(String key, String defaultValue) { - String result = (String) filters.get(key); - return result == null ? defaultValue : result; + String value = getStringFilter(key); + if (!StringUtils.hasText(value)) { + return defaultValue; + } + return value; } public int getIntFilter(String key) { - return Integer.parseInt((String) filters.get(key)); + Filter filter = (Filter) filters.get(key); + if (filter != null) { + String value = (String) filter.getValue()[0]; + if (StringUtils.hasText(value)) { + return Integer.parseInt(value); + } + return 0; + } + return 0; } public int getIntFilter(String key, int defaultValue) { - String result = (String) filters.get(key); - return result == null ? defaultValue : Integer.parseInt(result); + int value = getIntFilter(key); + if (value == 0) { + return defaultValue; + } + return value; } @@ -182,38 +196,96 @@ public void addSort(Sort sort) { } } - public PageRequest addFilter(String key, Object value) { - this.filters.put(key, value); + public PageRequest addFilter(String key, FilterRelation relation, Object... value) { + putFilter(key, relation, value); return this; } + public PageRequest addFilter(String key, Object... value) { + return this.addFilter(key, FilterRelation.EUQAL, value); + } + public boolean hasFilter() { return !this.filters.isEmpty(); } - public Example getExample(Class clazz) { - if (!hasFilter()) { - return null; + @Setter + @Getter + public static class Filter { + private String key; + private Object[] value; + + private FilterRelation relation; + + public boolean isEqual() { + return relation == FilterRelation.EUQAL; } - Object entity = null; - try { - entity = clazz.getDeclaredConstructor().newInstance(); - } catch (Exception e) { - throw new RuntimeException(e); - } - PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(clazz); - for (PropertyDescriptor descriptor : descriptors) { - String name = descriptor.getName(); - Object value = filters.get(name); - if (value != null) { - try { - descriptor.getWriteMethod().invoke(entity, value); - } catch (Exception e) { + + public boolean isLike() { + return relation == FilterRelation.LIKE; + } + + public boolean isBetween() { + return relation == FilterRelation.BETWEEN; + } + + public boolean isIn() { + return relation == FilterRelation.IN; + } + + public boolean isGreaterThan() { + return relation == FilterRelation.GREATER_THAN; + } + + public boolean isLessThan() { + return relation == FilterRelation.LESS_THAN; + } + + public boolean isGreaterThanEqual() { + return relation == FilterRelation.GREATER_THAN_EQUAL; + } + + public boolean isLessThanEqual() { + return relation == FilterRelation.LESS_THAN_EQUAL; + } + + public Object getFilterValue(Class clazz) { + Object val = value[0]; + if(val instanceof String) { + if(clazz == Integer.class) { + return Integer.parseInt((String)val); + } + if(clazz == Long.class) { + return Long.parseLong((String)val); + } + if(clazz == Double.class) { + return Double.parseDouble((String)val); + } + if(clazz == Float.class) { + return Float.parseFloat((String)val); } } + return value[0]; } - return (Example) Example.of(entity); } -} + private void putFilter(String key, FilterRelation relation, Object... val) { + Filter filter = new Filter(); + filter.setKey(key); + filter.setValue(val); + filter.setRelation(relation); + this.filters.put(key, filter); + } + + public enum FilterRelation { + EUQAL, + LIKE, + BETWEEN, + IN, + GREATER_THAN, + LESS_THAN, + GREATER_THAN_EQUAL, + LESS_THAN_EQUAL, + } +} \ No newline at end of file diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/rest/param/RestParam.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/rest/param/RestParam.java index a19efe09..7ad699a5 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/rest/param/RestParam.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/rest/param/RestParam.java @@ -2,11 +2,11 @@ import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; +import lombok.SneakyThrows; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; public class RestParam { @@ -66,9 +66,10 @@ public RestParam add(String key, Object value) { return add(key, value, true); } + @SneakyThrows public RestParam add(String key, Object value, boolean encode) { String stringValue = value.toString(); - String encodeValue = encode ? URLEncoder.encode(stringValue, StandardCharsets.UTF_8) : value.toString(); + String encodeValue = encode ? URLEncoder.encode(stringValue, "UTF-8") : value.toString(); jsonBody.put(key, value); mapBody.add(key, encodeValue); return this; diff --git a/springboot-starter/src/test/java/com/codingapi/springboot/framework/query/test/DemoRepositoryTest.java b/springboot-starter/src/test/java/com/codingapi/springboot/framework/query/test/DemoRepositoryTest.java index 3ae7c3e2..a35c577f 100644 --- a/springboot-starter/src/test/java/com/codingapi/springboot/framework/query/test/DemoRepositoryTest.java +++ b/springboot-starter/src/test/java/com/codingapi/springboot/framework/query/test/DemoRepositoryTest.java @@ -46,9 +46,9 @@ void query(){ request.setCurrent(1); request.setPageSize(10); - request.addFilter("name","123"); - - Example demo = request.getExample(Demo.class); + Demo search = new Demo(); + search.setName("123"); + Example demo = Example.of(search); System.out.println(demo); Page page = demoRepository.findAll(demo,request); assertEquals(1, page.getTotalElements()); From 3d2742a5d21da8a7810d423456b1689d77cbb11f Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Fri, 6 Oct 2023 11:06:21 +0800 Subject: [PATCH 003/101] add SortRepository & DynamicRepository --- .../fast/dynamic/DynamicConfiguration.java | 21 ++++++++ .../springboot/fast/dynamic/DynamicQuery.java | 50 +++++++++++++++++++ .../fast/dynamic/DynamicQueryContext.java | 23 +++++++++ .../dynamic/DynamicQueryContextRegister.java | 16 ++++++ .../fast/dynamic/DynamicRepository.java | 39 +++++++++++++++ .../springboot/fast/query/FastRepository.java | 7 +-- .../codingapi/springboot/fast/sort/ISort.java | 8 +++ .../springboot/fast/sort/SortRepository.java | 21 ++++++++ .../main/resources/META-INF/spring.factories | 3 +- ...ot.autoconfigure.AutoConfiguration.imports | 3 +- 10 files changed, 186 insertions(+), 5 deletions(-) create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicConfiguration.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQuery.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQueryContext.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQueryContextRegister.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicRepository.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/ISort.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/SortRepository.java diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicConfiguration.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicConfiguration.java new file mode 100644 index 00000000..1fe09262 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicConfiguration.java @@ -0,0 +1,21 @@ +package com.codingapi.springboot.fast.dynamic; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.persistence.EntityManager; + +@Configuration +public class DynamicConfiguration { + + @Bean + public DynamicQuery dynamicQuery(EntityManager entityManager){ + return new DynamicQuery(entityManager); + } + + @Bean + public DynamicQueryContextRegister dynamicQueryContextRegister(DynamicQuery dynamicQuery){ + return new DynamicQueryContextRegister(dynamicQuery); + } + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQuery.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQuery.java new file mode 100644 index 00000000..5727d337 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQuery.java @@ -0,0 +1,50 @@ +package com.codingapi.springboot.fast.dynamic; + +import lombok.AllArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; + +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; +import java.util.List; + +@AllArgsConstructor +public class DynamicQuery { + + private final EntityManager entityManager; + + public List listQuery(Class clazz, String sql, Object... params) { + TypedQuery query = entityManager.createQuery(sql, clazz); + if (params != null) { + for (int i = 0; i < params.length; i++) { + query.setParameter(i + 1, params[i]); + } + } + return query.getResultList(); + } + + public Page pageQuery(Class clazz, String sql, String countSql, PageRequest pageRequest, Object... params) { + TypedQuery query = entityManager.createQuery(sql, clazz); + if (params != null) { + for (int i = 0; i < params.length; i++) { + query.setParameter(i + 1, params[i]); + } + } + query.setFirstResult(pageRequest.getPageNumber() * pageRequest.getPageSize()); + query.setMaxResults(pageRequest.getPageSize()); + return new PageImpl<>(query.getResultList(), pageRequest, countQuery(countSql, params)); + } + + + private long countQuery(String sql, Object... params) { + TypedQuery query = entityManager.createQuery(sql, Long.class); + if (params != null) { + for (int i = 0; i < params.length; i++) { + query.setParameter(i + 1, params[i]); + } + } + return query.getSingleResult(); + } + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQueryContext.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQueryContext.java new file mode 100644 index 00000000..7e1e9230 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQueryContext.java @@ -0,0 +1,23 @@ +package com.codingapi.springboot.fast.dynamic; + + +import lombok.Getter; + +public class DynamicQueryContext { + + @Getter + private static final DynamicQueryContext instance = new DynamicQueryContext(); + + private DynamicQueryContext() { + + } + + @Getter + private DynamicQuery dynamicQuery; + + void setDynamicQuery(DynamicQuery dynamicQuery) { + this.dynamicQuery = dynamicQuery; + } + + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQueryContextRegister.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQueryContextRegister.java new file mode 100644 index 00000000..4ed1cb8d --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQueryContextRegister.java @@ -0,0 +1,16 @@ +package com.codingapi.springboot.fast.dynamic; + +import lombok.AllArgsConstructor; +import org.springframework.beans.factory.InitializingBean; + +@AllArgsConstructor +public class DynamicQueryContextRegister implements InitializingBean { + + private DynamicQuery dynamicQuery; + + @Override + public void afterPropertiesSet() throws Exception { + DynamicQueryContext.getInstance().setDynamicQuery(dynamicQuery); + } + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicRepository.java new file mode 100644 index 00000000..fffd31a4 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicRepository.java @@ -0,0 +1,39 @@ +package com.codingapi.springboot.fast.dynamic; + +import org.springframework.core.ResolvableType; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.NoRepositoryBean; + +import java.util.List; + +@NoRepositoryBean +public interface DynamicRepository extends JpaRepository { + + default Class getEntityClass() { + ResolvableType resolvableType = ResolvableType.forClass(this.getClass()).as(DynamicRepository.class); + return resolvableType.getGeneric(new int[]{0}).resolve(); + } + + default List dynamicListQuery(String sql, Object... params) { + return (List) DynamicQueryContext.getInstance().getDynamicQuery().listQuery(getEntityClass(), sql, params); + } + + default List dynamicListQuery(Class clazz, String sql, Object... params) { + return (List) DynamicQueryContext.getInstance().getDynamicQuery().listQuery(clazz, sql, params); + } + + default Page dynamicPageQuery(String sql, String countSql, PageRequest request, Object... params) { + return (Page) DynamicQueryContext.getInstance().getDynamicQuery().pageQuery(getEntityClass(), sql, countSql, request, params); + } + + default Page dynamicPageQuery(String sql, PageRequest request, Object... params) { + return (Page) DynamicQueryContext.getInstance().getDynamicQuery().pageQuery(getEntityClass(), sql, "select count(1) " + sql, request, params); + } + + default Page dynamicPageQuery(Class clazz, String sql, String countSql, PageRequest request, Object... params) { + return (Page) DynamicQueryContext.getInstance().getDynamicQuery().pageQuery(clazz, sql, countSql, request, params); + } + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/FastRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/FastRepository.java index 937b5928..5ee1f504 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/FastRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/FastRepository.java @@ -1,8 +1,7 @@ package com.codingapi.springboot.fast.query; +import com.codingapi.springboot.fast.dynamic.DynamicRepository; import com.codingapi.springboot.framework.dto.request.PageRequest; -import javax.persistence.criteria.Order; -import javax.persistence.criteria.Predicate; import org.springframework.core.ResolvableType; import org.springframework.data.domain.Page; import org.springframework.data.jpa.domain.Specification; @@ -10,10 +9,12 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.NoRepositoryBean; +import javax.persistence.criteria.Order; +import javax.persistence.criteria.Predicate; import java.util.List; @NoRepositoryBean -public interface FastRepository extends JpaRepository, JpaSpecificationExecutor { +public interface FastRepository extends JpaRepository, JpaSpecificationExecutor, DynamicRepository { default Page findAll(PageRequest request) { if (request.hasFilter()) { diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/ISort.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/ISort.java new file mode 100644 index 00000000..b4a689d3 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/ISort.java @@ -0,0 +1,8 @@ +package com.codingapi.springboot.fast.sort; + +public interface ISort { + + Integer getSort(); + + void setSort(Integer sort); +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/SortRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/SortRepository.java new file mode 100644 index 00000000..b13f5120 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/SortRepository.java @@ -0,0 +1,21 @@ +package com.codingapi.springboot.fast.sort; + +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.NoRepositoryBean; + +import java.util.List; + +@NoRepositoryBean +public interface SortRepository extends JpaRepository { + + + default void sort(PageRequest request, List ids) { + for (int i = 0; i < ids.size(); i++) { + ISort entity = getById((I) ids.get(i)); + entity.setSort(i + ((request.getPageNumber() - 1) * request.getPageSize())); + save((T) entity); + } + } + +} diff --git a/springboot-starter-data-fast/src/main/resources/META-INF/spring.factories b/springboot-starter-data-fast/src/main/resources/META-INF/spring.factories index 545e7ce2..99726f46 100644 --- a/springboot-starter-data-fast/src/main/resources/META-INF/spring.factories +++ b/springboot-starter-data-fast/src/main/resources/META-INF/spring.factories @@ -1,3 +1,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.codingapi.springboot.fast.DataFastConfiguration,\ -com.codingapi.springboot.fast.registrar.DataFastBeanDefinitionRegistrar \ No newline at end of file +com.codingapi.springboot.fast.registrar.DataFastBeanDefinitionRegistrar,\ +com.codingapi.springboot.fast.dynamic.DynamicConfiguration diff --git a/springboot-starter-data-fast/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/springboot-starter-data-fast/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index c1debcb6..e8f74008 100644 --- a/springboot-starter-data-fast/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/springboot-starter-data-fast/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,2 +1,3 @@ com.codingapi.springboot.fast.DataFastConfiguration -com.codingapi.springboot.fast.registrar.DataFastBeanDefinitionRegistrar \ No newline at end of file +com.codingapi.springboot.fast.registrar.DataFastBeanDefinitionRegistrar +com.codingapi.springboot.fast.dynamic.DynamicConfiguration \ No newline at end of file From ed49e4264a4ed430550d5530935bbaee7b052c8d Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Fri, 6 Oct 2023 19:14:21 +0800 Subject: [PATCH 004/101] fix PageRequest.java --- docs/wiki/springboot-starter-data-fast.md | 159 ++++++++++++++++++ .../springboot/fast/query/FastRepository.java | 2 +- .../springboot/fast/sort/SortRepository.java | 15 +- .../springboot/fast/DemoRepositoryTest.java | 97 ++++++++++- .../springboot/fast/entity/Demo.java | 6 +- .../fast/repository/DemoRepository.java | 3 +- .../framework/dto/request/PageRequest.java | 31 ++-- 7 files changed, 285 insertions(+), 28 deletions(-) diff --git a/docs/wiki/springboot-starter-data-fast.md b/docs/wiki/springboot-starter-data-fast.md index 0466431d..154795dc 100644 --- a/docs/wiki/springboot-starter-data-fast.md +++ b/docs/wiki/springboot-starter-data-fast.md @@ -2,6 +2,7 @@ springboot-starter-data-fast 基于JPA的快速API能力服务 +## FastController 快速API能力服务 ```java package com.codingapi.springboot.example.query; @@ -45,3 +46,161 @@ value为查询语句,countQuery为查询总数语句,query为查询参数, MultiResponse为返回结果 @PreAuthorize(value = "hasRole('ROLE_USER')") 用于标记当前接口的权限,如果不需要权限可以不用添加 +## FastRepository 的使用教程 + + +继承FastRepository接口,实现自定义的接口,即可使用FastRepository的能力 +```java + + +import com.codingapi.springboot.fast.entity.Demo; +import com.codingapi.springboot.fast.query.FastRepository; + +public interface DemoRepository extends FastRepository { + +} + + +``` +动态FastRepository的能力展示 + +```java + + // 重写findAll,通过Example查询 + @Test + void findAll() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demo2.setName("456"); + demoRepository.save(demo2); + + PageRequest request = new PageRequest(); + request.setCurrent(1); + request.setPageSize(10); + request.addFilter("name", "123"); + + Page page = demoRepository.findAll(request); + assertEquals(1, page.getTotalElements()); + } + + + // pageRequest 自定义条件查询 + @Test + void pageRequest() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demo2.setName("456"); + demoRepository.save(demo2); + + PageRequest request = new PageRequest(); + request.setCurrent(1); + request.setPageSize(10); + request.addFilter("name", PageRequest.FilterRelation.LIKE, "%2%"); + + Page page = demoRepository.pageRequest(request); + assertEquals(1, page.getTotalElements()); + } + + + // 动态sql的List查询 + @Test + void dynamicListQuery() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demo2.setName("456"); + demoRepository.save(demo2); + + List list = demoRepository.dynamicListQuery("from Demo where name = ?1", "123"); + assertEquals(1, list.size()); + } + + + // 动态sql的分页查询 + @Test + void dynamicPageQuery() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demo2.setName("456"); + demoRepository.save(demo2); + + Page page = demoRepository.dynamicPageQuery("from Demo where name = ?1", PageRequest.of(1, 2), "123"); + assertEquals(1, page.getTotalElements()); + } + + // 增加排序查询 + @Test + void sortQuery() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demo2.setName("456"); + demoRepository.save(demo2); + + PageRequest request = new PageRequest(); + request.setCurrent(1); + request.setPageSize(10); + + request.addSort(Sort.by("id").descending()); + Page page = demoRepository.findAll(request); + assertEquals(page.getContent().get(0).getName(), "456"); + assertEquals(2, page.getTotalElements()); + } + +``` +## SortRepository的使用教程 + +```java + +public interface DemoRepository extends FastRepository, SortRepository { + +} + +``` + +SortRepository的能力展示 + +```java + + @Test + @Transactional + void pageSort() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demo2.setName("456"); + demoRepository.save(demo2); + + List ids = Arrays.asList(demo1.getId(), demo2.getId()); + System.out.println(ids); + demoRepository.pageSort(PageRequest.of(1, 10), ids); + + Demo newDemo1 = demoRepository.getReferenceById(demo1.getId()); + Demo newDemo2 = demoRepository.getReferenceById(demo2.getId()); + + assertEquals(newDemo2.getSort(), 1); + assertEquals(newDemo1.getSort(), 0); + } + +``` \ No newline at end of file diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/FastRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/FastRepository.java index 5ee1f504..c1a4374d 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/FastRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/FastRepository.java @@ -33,7 +33,7 @@ default Class getDomainClass() { } - default Page findAllByRequest(PageRequest request) { + default Page pageRequest(PageRequest request) { if (request.hasFilter()) { Class clazz = getDomainClass(); Specification specification = (root, query, criteriaBuilder) -> { diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/SortRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/SortRepository.java index b13f5120..499ecd34 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/SortRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/SortRepository.java @@ -1,21 +1,24 @@ package com.codingapi.springboot.fast.sort; -import org.springframework.data.domain.PageRequest; +import com.codingapi.springboot.framework.dto.request.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.NoRepositoryBean; +import java.util.ArrayList; import java.util.List; @NoRepositoryBean -public interface SortRepository extends JpaRepository { +public interface SortRepository extends JpaRepository { - default void sort(PageRequest request, List ids) { + default void pageSort(PageRequest request, List ids) { + List list = new ArrayList<>(); for (int i = 0; i < ids.size(); i++) { - ISort entity = getById((I) ids.get(i)); - entity.setSort(i + ((request.getPageNumber() - 1) * request.getPageSize())); - save((T) entity); + ISort entity = getReferenceById(ids.get(i)); + entity.setSort(i + (request.getPageNumber() * request.getPageSize())); + list.add((T) entity); } + saveAll(list); } } diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java index 283fd318..c1e74ebb 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java @@ -8,6 +8,10 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.Sort; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -20,17 +24,17 @@ public class DemoRepositoryTest { @Test - void test(){ + void test() { demoRepository.deleteAll(); Demo demo = new Demo(); demo.setName("123"); demoRepository.save(demo); - assertTrue(demo.getId()>0); + assertTrue(demo.getId() > 0); } @Test - void query(){ + void findAll() { demoRepository.deleteAll(); Demo demo1 = new Demo(); demo1.setName("123"); @@ -43,15 +47,68 @@ void query(){ PageRequest request = new PageRequest(); request.setCurrent(1); request.setPageSize(10); - request.addFilter("name","123"); + request.addFilter("name", "123"); - Page page = demoRepository.findAll(request); + Page page = demoRepository.findAll(request); assertEquals(1, page.getTotalElements()); } @Test - void sort(){ + void pageRequest() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demo2.setName("456"); + demoRepository.save(demo2); + + PageRequest request = new PageRequest(); + request.setCurrent(1); + request.setPageSize(10); + request.addFilter("name", PageRequest.FilterRelation.LIKE, "%2%"); + + Page page = demoRepository.pageRequest(request); + assertEquals(1, page.getTotalElements()); + } + + + @Test + void dynamicListQuery() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demo2.setName("456"); + demoRepository.save(demo2); + + List list = demoRepository.dynamicListQuery("from Demo where name = ?1", "123"); + assertEquals(1, list.size()); + } + + + @Test + void dynamicPageQuery() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demo2.setName("456"); + demoRepository.save(demo2); + + Page page = demoRepository.dynamicPageQuery("from Demo where name = ?1", PageRequest.of(1, 2), "123"); + assertEquals(1, page.getTotalElements()); + } + + + @Test + void sortQuery() { demoRepository.deleteAll(); Demo demo1 = new Demo(); demo1.setName("123"); @@ -66,8 +123,32 @@ void sort(){ request.setPageSize(10); request.addSort(Sort.by("id").descending()); - Page page = demoRepository.findAll(request); - assertEquals(page.getContent().get(0).getName(),"456"); + Page page = demoRepository.findAll(request); + assertEquals(page.getContent().get(0).getName(), "456"); assertEquals(2, page.getTotalElements()); } + + + @Test + @Transactional + void pageSort() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demo2.setName("456"); + demoRepository.save(demo2); + + List ids = Arrays.asList(demo1.getId(), demo2.getId()); + System.out.println(ids); + demoRepository.pageSort(PageRequest.of(1, 10), ids); + + Demo newDemo1 = demoRepository.getReferenceById(demo1.getId()); + Demo newDemo2 = demoRepository.getReferenceById(demo2.getId()); + + assertEquals(newDemo2.getSort(), 1); + assertEquals(newDemo1.getSort(), 0); + } } diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Demo.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Demo.java index a39afa5d..4de36433 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Demo.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Demo.java @@ -1,5 +1,6 @@ package com.codingapi.springboot.fast.entity; +import com.codingapi.springboot.fast.sort.ISort; import lombok.Getter; import lombok.Setter; @@ -10,10 +11,13 @@ @Getter @Entity @Table(name = "t_demo") -public class Demo { +public class Demo implements ISort { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; + + private Integer sort; } diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/DemoRepository.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/DemoRepository.java index 43b36280..9f5c76af 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/DemoRepository.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/DemoRepository.java @@ -2,7 +2,8 @@ import com.codingapi.springboot.fast.entity.Demo; import com.codingapi.springboot.fast.query.FastRepository; +import com.codingapi.springboot.fast.sort.SortRepository; -public interface DemoRepository extends FastRepository { +public interface DemoRepository extends FastRepository, SortRepository { } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java index 42e71cef..a8cdfa7d 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java @@ -1,6 +1,5 @@ package com.codingapi.springboot.framework.dto.request; -import javax.servlet.http.HttpServletRequest; import lombok.Getter; import lombok.Setter; import org.springframework.data.domain.Pageable; @@ -9,6 +8,7 @@ import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import javax.servlet.http.HttpServletRequest; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; @@ -32,7 +32,7 @@ public PageRequest(int current, int pageSize, Sort sort) { super(current > 0 ? current-- : 0, pageSize, sort); this.current = current; this.pageSize = pageSize; - this.pageRequest = PageRequest.of(current, pageSize, sort); + this.pageRequest = org.springframework.data.domain.PageRequest.of(current, pageSize, sort); try { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); @@ -251,18 +251,18 @@ public boolean isLessThanEqual() { public Object getFilterValue(Class clazz) { Object val = value[0]; - if(val instanceof String) { - if(clazz == Integer.class) { - return Integer.parseInt((String)val); + if (val instanceof String) { + if (clazz == Integer.class) { + return Integer.parseInt((String) val); } - if(clazz == Long.class) { - return Long.parseLong((String)val); + if (clazz == Long.class) { + return Long.parseLong((String) val); } - if(clazz == Double.class) { - return Double.parseDouble((String)val); + if (clazz == Double.class) { + return Double.parseDouble((String) val); } - if(clazz == Float.class) { - return Float.parseFloat((String)val); + if (clazz == Float.class) { + return Float.parseFloat((String) val); } } return value[0]; @@ -288,4 +288,13 @@ public enum FilterRelation { GREATER_THAN_EQUAL, LESS_THAN_EQUAL, } + + + public static PageRequest of(int page, int size) { + return new PageRequest(page, size, Sort.unsorted()); + } + + public static PageRequest of(int page, int size, Sort sort) { + return new PageRequest(page, size, sort); + } } \ No newline at end of file From 5eabfdaef22bff821707640abff1804a628c71f5 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Wed, 11 Oct 2023 22:49:02 +0800 Subject: [PATCH 005/101] fix version --- .../springboot/security/controller/VersionController.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/controller/VersionController.java b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/controller/VersionController.java index 7d2385a8..03e8445d 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/controller/VersionController.java +++ b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/controller/VersionController.java @@ -1,5 +1,6 @@ package com.codingapi.springboot.security.controller; +import com.codingapi.springboot.framework.dto.response.SingleResponse; import lombok.AllArgsConstructor; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; @@ -14,7 +15,7 @@ public class VersionController { private final Environment env; @GetMapping("/version") - public String version(){ - return env.getProperty("application.version","-"); + public SingleResponse version() { + return SingleResponse.of(env.getProperty("application.version", "-")); } } From 8302e6471f6c4b96ffd73fdba9e01e149b2f9a90 Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Thu, 26 Oct 2023 23:54:55 +0800 Subject: [PATCH 006/101] add or search --- .../springboot/fast/query/QueryRequest.java | 192 +++++++++++------- .../springboot/fast/DemoRepositoryTest.java | 20 ++ .../framework/dto/request/PageRequest.java | 14 ++ 3 files changed, 149 insertions(+), 77 deletions(-) diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java index 32056ce4..17ab2575 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java @@ -1,13 +1,13 @@ package com.codingapi.springboot.fast.query; import com.codingapi.springboot.framework.dto.request.PageRequest; +import org.springframework.beans.BeanUtils; +import org.springframework.data.domain.Example; + import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.Order; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; -import org.springframework.beans.BeanUtils; -import org.springframework.data.domain.Example; - import java.beans.PropertyDescriptor; import java.util.ArrayList; import java.util.Date; @@ -48,6 +48,16 @@ public Example getExample() { } + private List getClazzProperties() { + List properties = new ArrayList<>(); + PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(clazz); + for (PropertyDescriptor descriptor : descriptors) { + properties.add(descriptor.getName()); + } + return properties; + } + + public List getOrder(Root root, CriteriaBuilder criteriaBuilder) { List orderList = new ArrayList<>(); request.getSort().forEach(sort -> { @@ -62,82 +72,110 @@ public List getOrder(Root root, CriteriaBuilder criteriaBuilder) { public List getPredicate(Root root, CriteriaBuilder criteriaBuilder) { List predicates = new ArrayList<>(); + List properties = getClazzProperties(); for (String key : request.getFilters().keySet()) { PageRequest.Filter filter = request.getFilters().get(key); - if (filter.isEqual()) { - predicates.add(criteriaBuilder.equal(root.get(key), filter.getValue()[0])); - } - - if (filter.isLike()) { - String matchValue = (String) filter.getValue()[0]; - predicates.add(criteriaBuilder.like(root.get(key), "%" + matchValue + "%")); - } - - if (filter.isBetween()) { - Object value1 = filter.getValue()[0]; - Object value2 = filter.getValue()[2]; - if (value1 instanceof Integer && value2 instanceof Integer) { - predicates.add(criteriaBuilder.between(root.get(key), (Integer) value1, (Integer) value2)); - } - - if (value1 instanceof Long && value2 instanceof Long) { - predicates.add(criteriaBuilder.between(root.get(key), (Long) value1, (Long) value2)); - } - - if (value1 instanceof Date && value2 instanceof Date) { - predicates.add(criteriaBuilder.between(root.get(key), (Date) value1, (Date) value2)); - } - } - - if (filter.isGreaterThan()) { - Object value = filter.getValue()[0]; - if (value instanceof Integer) { - predicates.add(criteriaBuilder.greaterThan(root.get(key), (Integer) value)); - } - if (value instanceof Long) { - predicates.add(criteriaBuilder.greaterThan(root.get(key), (Long) value)); - } - if (value instanceof Date) { - predicates.add(criteriaBuilder.greaterThan(root.get(key), (Date) value)); - } - } - - if (filter.isGreaterThanEqual()) { - Object value = filter.getValue()[0]; - if (value instanceof Integer) { - predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Integer) value)); - } - if (value instanceof Long) { - predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Long) value)); - } - if (value instanceof Date) { - predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Date) value)); - } - } - - if (filter.isLessThan()) { - Object value = filter.getValue()[0]; - if (value instanceof Integer) { - predicates.add(criteriaBuilder.lessThan(root.get(key), (Integer) value)); - } - if (value instanceof Long) { - predicates.add(criteriaBuilder.lessThan(root.get(key), (Long) value)); - } - if (value instanceof Date) { - predicates.add(criteriaBuilder.lessThan(root.get(key), (Date) value)); - } - } - - if (filter.isLessThanEqual()) { - Object value = filter.getValue()[0]; - if (value instanceof Integer) { - predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get(key), (Integer) value)); - } - if (value instanceof Long) { - predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get(key), (Long) value)); - } - if (value instanceof Date) { - predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get(key), (Date) value)); + if (filter.isOr() || properties.contains(key)) { + if (filter.isEqual()) { + predicates.add(criteriaBuilder.equal(root.get(key), filter.getValue()[0])); + } + + if (filter.isLike()) { + String matchValue = (String) filter.getValue()[0]; + predicates.add(criteriaBuilder.like(root.get(key), "%" + matchValue + "%")); + } + + if (filter.isBetween()) { + Object value1 = filter.getValue()[0]; + Object value2 = filter.getValue()[2]; + if (value1 instanceof Integer && value2 instanceof Integer) { + predicates.add(criteriaBuilder.between(root.get(key), (Integer) value1, (Integer) value2)); + } + + if (value1 instanceof Long && value2 instanceof Long) { + predicates.add(criteriaBuilder.between(root.get(key), (Long) value1, (Long) value2)); + } + + if (value1 instanceof Date && value2 instanceof Date) { + predicates.add(criteriaBuilder.between(root.get(key), (Date) value1, (Date) value2)); + } + } + + if (filter.isGreaterThan()) { + Object value = filter.getValue()[0]; + if (value instanceof Integer) { + predicates.add(criteriaBuilder.greaterThan(root.get(key), (Integer) value)); + } + if (value instanceof Long) { + predicates.add(criteriaBuilder.greaterThan(root.get(key), (Long) value)); + } + if (value instanceof Date) { + predicates.add(criteriaBuilder.greaterThan(root.get(key), (Date) value)); + } + } + + if (filter.isGreaterThanEqual()) { + Object value = filter.getValue()[0]; + if (value instanceof Integer) { + predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Integer) value)); + } + if (value instanceof Long) { + predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Long) value)); + } + if (value instanceof Date) { + predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Date) value)); + } + } + + if (filter.isLessThan()) { + Object value = filter.getValue()[0]; + if (value instanceof Integer) { + predicates.add(criteriaBuilder.lessThan(root.get(key), (Integer) value)); + } + if (value instanceof Long) { + predicates.add(criteriaBuilder.lessThan(root.get(key), (Long) value)); + } + if (value instanceof Date) { + predicates.add(criteriaBuilder.lessThan(root.get(key), (Date) value)); + } + } + + if (filter.isLessThanEqual()) { + Object value = filter.getValue()[0]; + if (value instanceof Integer) { + predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get(key), (Integer) value)); + } + if (value instanceof Long) { + predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get(key), (Long) value)); + } + if (value instanceof Date) { + predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get(key), (Date) value)); + } + } + + if (filter.isIn()) { + Object[] value = filter.getValue(); + predicates.add(criteriaBuilder.in(root.get(key)).value(value)); + } + + if (filter.isOr()) { + if(key.equals("OR")) { + Object[] value = filter.getValue(); + + // 创建Predicate的列表,用于收集所有的OR条件 + List orPredicates = new ArrayList<>(); + + // 循环遍历value数组,每两个为一组 + for (int i = 0; i < value.length; i += 2) { + orPredicates.add(criteriaBuilder.equal(root.get((String) value[i]), value[i + 1])); + } + + // 使用or方法连接所有的Predicate + predicates.add(criteriaBuilder.or(orPredicates.toArray(new Predicate[0]))); + }else{ + Object[] value = filter.getValue(); + predicates.add(criteriaBuilder.equal(root.get(key), value[0])); + } } } } diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java index c1e74ebb..5ba19548 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java @@ -74,6 +74,26 @@ void pageRequest() { assertEquals(1, page.getTotalElements()); } + @Test + void pageRequestOr() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demo2.setName("456"); + demoRepository.save(demo2); + + PageRequest request = new PageRequest(); + request.setCurrent(1); + request.setPageSize(10); + request.addOrFilters("name", "%2%","id","1"); + + Page page = demoRepository.pageRequest(request); + assertEquals(1, page.getTotalElements()); + } + @Test void dynamicListQuery() { diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java index a8cdfa7d..312a8dbe 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java @@ -205,6 +205,15 @@ public PageRequest addFilter(String key, Object... value) { return this.addFilter(key, FilterRelation.EUQAL, value); } + public PageRequest addOrFilters(Object... value) { + // 检查value数组是否为偶数长度,因为我们需要成对处理它 + if (value.length % 2 != 0) { + throw new IllegalArgumentException("Invalid number of elements in value array."); + } + + return this.addFilter("OR", FilterRelation.OR, value); + } + public boolean hasFilter() { return !this.filters.isEmpty(); } @@ -233,6 +242,10 @@ public boolean isIn() { return relation == FilterRelation.IN; } + public boolean isOr() { + return relation == FilterRelation.OR; + } + public boolean isGreaterThan() { return relation == FilterRelation.GREATER_THAN; } @@ -287,6 +300,7 @@ public enum FilterRelation { LESS_THAN, GREATER_THAN_EQUAL, LESS_THAN_EQUAL, + OR, } From a037cddfc509b932d1d9cd1257b41d21c5bd9bf2 Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Fri, 27 Oct 2023 17:36:03 +0800 Subject: [PATCH 007/101] add AuthenticationTokenFilter --- .../security/AutoConfiguration.java | 38 +++++++++++++++---- .../configurer/HttpSecurityConfigurer.java | 4 +- .../filter/AuthenticationTokenFilter.java | 12 ++++++ .../filter/MyAuthenticationFilter.java | 5 ++- .../security/filter/MyLoginFilter.java | 4 +- .../security/filter/SecurityLoginHandler.java | 5 ++- 6 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/AuthenticationTokenFilter.java diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java index 7587aaa0..fb557e55 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java +++ b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java @@ -2,8 +2,10 @@ import com.codingapi.springboot.security.configurer.HttpSecurityConfigurer; import com.codingapi.springboot.security.controller.VersionController; +import com.codingapi.springboot.security.dto.request.LoginRequest; import com.codingapi.springboot.security.filter.*; import com.codingapi.springboot.security.jwt.Jwt; +import com.codingapi.springboot.security.jwt.Token; import com.codingapi.springboot.security.properties.SecurityJwtProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -24,6 +26,9 @@ import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + @Configuration @EnableWebSecurity public class AutoConfiguration { @@ -53,28 +58,45 @@ public PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } + @Bean + @ConditionalOnMissingBean + public AuthenticationTokenFilter authenticationTokenFilter() { + return (request, response, chain) -> { + + }; + } + @Bean @ConditionalOnMissingBean - public SecurityLoginHandler securityLoginHandler(){ - return (request, response, handler) -> { + public SecurityLoginHandler securityLoginHandler() { + return new SecurityLoginHandler() { + @Override + public void preHandle(HttpServletRequest request, HttpServletResponse response, LoginRequest handler) throws Exception { + + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, LoginRequest handler, Token token) { + + } }; } @Bean @ConditionalOnMissingBean - public SecurityFilterChain filterChain(HttpSecurity security, Jwt jwt,SecurityLoginHandler loginHandler, - SecurityJwtProperties properties) throws Exception { + public SecurityFilterChain filterChain(HttpSecurity security, Jwt jwt, SecurityLoginHandler loginHandler, + SecurityJwtProperties properties, AuthenticationTokenFilter authenticationTokenFilter) throws Exception { //disable basic auth security.httpBasic().disable(); //before add addCorsMappings to enable cors. security.cors(); - if(properties.isDisableCsrf() ){ + if (properties.isDisableCsrf()) { security.csrf().disable(); } - security.apply(new HttpSecurityConfigurer(jwt,loginHandler,properties)); + security.apply(new HttpSecurityConfigurer(jwt, loginHandler, properties, authenticationTokenFilter)); security .exceptionHandling() .authenticationEntryPoint(new MyUnAuthenticationEntryPoint()) @@ -125,7 +147,7 @@ public WebMvcConfigurer corsConfigurer(SecurityJwtProperties securityJwtProperti return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { - if(securityJwtProperties.isDisableCors()) { + if (securityJwtProperties.isDisableCors()) { registry.addMapping("/**") .allowedHeaders("*") .allowedMethods("*") @@ -149,7 +171,7 @@ public SecurityJwtProperties securityJwtProperties() { @Bean @ConditionalOnMissingBean - public VersionController versionController(Environment environment){ + public VersionController versionController(Environment environment) { return new VersionController(environment); } diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/configurer/HttpSecurityConfigurer.java b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/configurer/HttpSecurityConfigurer.java index d46a3a0b..c95ec34e 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/configurer/HttpSecurityConfigurer.java +++ b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/configurer/HttpSecurityConfigurer.java @@ -1,5 +1,6 @@ package com.codingapi.springboot.security.configurer; +import com.codingapi.springboot.security.filter.AuthenticationTokenFilter; import com.codingapi.springboot.security.filter.MyAuthenticationFilter; import com.codingapi.springboot.security.filter.MyLoginFilter; import com.codingapi.springboot.security.filter.SecurityLoginHandler; @@ -17,11 +18,12 @@ public class HttpSecurityConfigurer extends AbstractHttpConfigurer Date: Sat, 28 Oct 2023 00:15:36 +0800 Subject: [PATCH 008/101] fix Filter --- .../springboot/fast/query/QueryRequest.java | 228 +++++++++--------- .../springboot/fast/DemoRepositoryTest.java | 11 +- .../framework/dto/request/Filter.java | 96 ++++++++ .../framework/dto/request/PageRequest.java | 161 ++----------- .../framework/dto/request/Relation.java | 13 + .../framework/dto/request/RequestFilter.java | 95 ++++++++ 6 files changed, 346 insertions(+), 258 deletions(-) create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Relation.java create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java index 17ab2575..ff794568 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java @@ -1,13 +1,16 @@ package com.codingapi.springboot.fast.query; -import com.codingapi.springboot.framework.dto.request.PageRequest; -import org.springframework.beans.BeanUtils; -import org.springframework.data.domain.Example; +import com.codingapi.springboot.framework.dto.request.Filter; +import com.codingapi.springboot.framework.dto.request.PageRequest; +import com.codingapi.springboot.framework.dto.request.RequestFilter; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.Order; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import org.springframework.beans.BeanUtils; +import org.springframework.data.domain.Example; + import java.beans.PropertyDescriptor; import java.util.ArrayList; import java.util.Date; @@ -24,7 +27,8 @@ public QueryRequest(PageRequest request, Class clazz) { } public Example getExample() { - if (!request.hasFilter()) { + RequestFilter requestFilter = request.getRequestFilter(); + if (!requestFilter.hasFilter()) { return null; } Object entity = null; @@ -36,7 +40,7 @@ public Example getExample() { PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(clazz); for (PropertyDescriptor descriptor : descriptors) { String name = descriptor.getName(); - PageRequest.Filter value = request.getFilters().get(name); + Filter value = requestFilter.getFilter(name); if (value != null) { try { descriptor.getWriteMethod().invoke(entity, value.getFilterValue(descriptor.getPropertyType())); @@ -70,116 +74,120 @@ public List getOrder(Root root, CriteriaBuilder criteriaBuilder) { return orderList; } + + private Predicate toPredicate(Filter filter, CriteriaBuilder criteriaBuilder, Root root, List properties) { + String key = filter.getKey(); + if (filter.isOr() || properties.contains(key)) { + + if (filter.isEqual()) { + return criteriaBuilder.equal(root.get(key), filter.getValue()[0]); + } + + if (filter.isLike()) { + String matchValue = (String) filter.getValue()[0]; + return criteriaBuilder.like(root.get(key), "%" + matchValue + "%"); + } + + if (filter.isBetween()) { + Object value1 = filter.getValue()[0]; + Object value2 = filter.getValue()[2]; + if (value1 instanceof Integer && value2 instanceof Integer) { + return criteriaBuilder.between(root.get(key), (Integer) value1, (Integer) value2); + } + + if (value1 instanceof Long && value2 instanceof Long) { + return criteriaBuilder.between(root.get(key), (Long) value1, (Long) value2); + } + + if (value1 instanceof Date && value2 instanceof Date) { + return criteriaBuilder.between(root.get(key), (Date) value1, (Date) value2); + } + } + + if (filter.isGreaterThan()) { + Object value = filter.getValue()[0]; + if (value instanceof Integer) { + return criteriaBuilder.greaterThan(root.get(key), (Integer) value); + } + if (value instanceof Long) { + return criteriaBuilder.greaterThan(root.get(key), (Long) value); + } + if (value instanceof Date) { + return criteriaBuilder.greaterThan(root.get(key), (Date) value); + } + } + + if (filter.isGreaterThanEqual()) { + Object value = filter.getValue()[0]; + if (value instanceof Integer) { + return criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Integer) value); + } + if (value instanceof Long) { + return criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Long) value); + } + if (value instanceof Date) { + return criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Date) value); + } + } + + if (filter.isLessThan()) { + Object value = filter.getValue()[0]; + if (value instanceof Integer) { + return criteriaBuilder.lessThan(root.get(key), (Integer) value); + } + if (value instanceof Long) { + return criteriaBuilder.lessThan(root.get(key), (Long) value); + } + if (value instanceof Date) { + return criteriaBuilder.lessThan(root.get(key), (Date) value); + } + } + + if (filter.isLessThanEqual()) { + Object value = filter.getValue()[0]; + if (value instanceof Integer) { + return criteriaBuilder.lessThanOrEqualTo(root.get(key), (Integer) value); + } + if (value instanceof Long) { + return criteriaBuilder.lessThanOrEqualTo(root.get(key), (Long) value); + } + if (value instanceof Date) { + return criteriaBuilder.lessThanOrEqualTo(root.get(key), (Date) value); + } + } + + if (filter.isIn()) { + Object[] value = filter.getValue(); + CriteriaBuilder.In in = criteriaBuilder.in(root.get(key)); + for (Object item : value) { + in.value(item); + } + return in; + } + + if (filter.isOr()) { + Filter[] orFilters = (Filter[]) filter.getValue(); + List orPredicates = new ArrayList<>(); + for (Filter orFilter : orFilters) { + orPredicates.add(toPredicate(orFilter, criteriaBuilder, root, properties)); + } + return criteriaBuilder.or(orPredicates.toArray(new Predicate[0])); + } + } + return null; + } + + public List getPredicate(Root root, CriteriaBuilder criteriaBuilder) { List predicates = new ArrayList<>(); List properties = getClazzProperties(); - for (String key : request.getFilters().keySet()) { - PageRequest.Filter filter = request.getFilters().get(key); - if (filter.isOr() || properties.contains(key)) { - if (filter.isEqual()) { - predicates.add(criteriaBuilder.equal(root.get(key), filter.getValue()[0])); - } - - if (filter.isLike()) { - String matchValue = (String) filter.getValue()[0]; - predicates.add(criteriaBuilder.like(root.get(key), "%" + matchValue + "%")); - } - - if (filter.isBetween()) { - Object value1 = filter.getValue()[0]; - Object value2 = filter.getValue()[2]; - if (value1 instanceof Integer && value2 instanceof Integer) { - predicates.add(criteriaBuilder.between(root.get(key), (Integer) value1, (Integer) value2)); - } - - if (value1 instanceof Long && value2 instanceof Long) { - predicates.add(criteriaBuilder.between(root.get(key), (Long) value1, (Long) value2)); - } - - if (value1 instanceof Date && value2 instanceof Date) { - predicates.add(criteriaBuilder.between(root.get(key), (Date) value1, (Date) value2)); - } - } - - if (filter.isGreaterThan()) { - Object value = filter.getValue()[0]; - if (value instanceof Integer) { - predicates.add(criteriaBuilder.greaterThan(root.get(key), (Integer) value)); - } - if (value instanceof Long) { - predicates.add(criteriaBuilder.greaterThan(root.get(key), (Long) value)); - } - if (value instanceof Date) { - predicates.add(criteriaBuilder.greaterThan(root.get(key), (Date) value)); - } - } - - if (filter.isGreaterThanEqual()) { - Object value = filter.getValue()[0]; - if (value instanceof Integer) { - predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Integer) value)); - } - if (value instanceof Long) { - predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Long) value)); - } - if (value instanceof Date) { - predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Date) value)); - } - } - - if (filter.isLessThan()) { - Object value = filter.getValue()[0]; - if (value instanceof Integer) { - predicates.add(criteriaBuilder.lessThan(root.get(key), (Integer) value)); - } - if (value instanceof Long) { - predicates.add(criteriaBuilder.lessThan(root.get(key), (Long) value)); - } - if (value instanceof Date) { - predicates.add(criteriaBuilder.lessThan(root.get(key), (Date) value)); - } - } - - if (filter.isLessThanEqual()) { - Object value = filter.getValue()[0]; - if (value instanceof Integer) { - predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get(key), (Integer) value)); - } - if (value instanceof Long) { - predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get(key), (Long) value)); - } - if (value instanceof Date) { - predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get(key), (Date) value)); - } - } - - if (filter.isIn()) { - Object[] value = filter.getValue(); - predicates.add(criteriaBuilder.in(root.get(key)).value(value)); - } - - if (filter.isOr()) { - if(key.equals("OR")) { - Object[] value = filter.getValue(); - - // 创建Predicate的列表,用于收集所有的OR条件 - List orPredicates = new ArrayList<>(); - - // 循环遍历value数组,每两个为一组 - for (int i = 0; i < value.length; i += 2) { - orPredicates.add(criteriaBuilder.equal(root.get((String) value[i]), value[i + 1])); - } - - // 使用or方法连接所有的Predicate - predicates.add(criteriaBuilder.or(orPredicates.toArray(new Predicate[0]))); - }else{ - Object[] value = filter.getValue(); - predicates.add(criteriaBuilder.equal(root.get(key), value[0])); - } - } + RequestFilter requestFilter = request.getRequestFilter(); + for (Filter filter : requestFilter.getFilters()) { + Predicate predicate = toPredicate(filter, criteriaBuilder, root, properties); + if (predicate != null) { + predicates.add(predicate); } } - return predicates; } } \ No newline at end of file diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java index 5ba19548..2cf69b91 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java @@ -2,7 +2,9 @@ import com.codingapi.springboot.fast.entity.Demo; import com.codingapi.springboot.fast.repository.DemoRepository; +import com.codingapi.springboot.framework.dto.request.Filter; import com.codingapi.springboot.framework.dto.request.PageRequest; +import com.codingapi.springboot.framework.dto.request.Relation; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -68,14 +70,14 @@ void pageRequest() { PageRequest request = new PageRequest(); request.setCurrent(1); request.setPageSize(10); - request.addFilter("name", PageRequest.FilterRelation.LIKE, "%2%"); + request.addFilter("name", Relation.LIKE, "%2%"); Page page = demoRepository.pageRequest(request); assertEquals(1, page.getTotalElements()); } @Test - void pageRequestOr() { + void customSearchOr() { demoRepository.deleteAll(); Demo demo1 = new Demo(); demo1.setName("123"); @@ -88,10 +90,11 @@ void pageRequestOr() { PageRequest request = new PageRequest(); request.setCurrent(1); request.setPageSize(10); - request.addOrFilters("name", "%2%","id","1"); + + request.orFilters(Filter.as("name", Relation.LIKE, "%2%"), Filter.as("id", Relation.IN, 1, 2, 3)); Page page = demoRepository.pageRequest(request); - assertEquals(1, page.getTotalElements()); + assertEquals(2, page.getTotalElements()); } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java new file mode 100644 index 00000000..eb143b60 --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java @@ -0,0 +1,96 @@ +package com.codingapi.springboot.framework.dto.request; + + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class Filter { + + private String key; + private Object[] value; + private Relation relation; + + public Filter(String key, Relation relation, Object... value) { + this.key = key; + this.value = value; + this.relation = relation; + } + + public Filter(String key, Object... value) { + this(key, Relation.EQUAL, value); + } + + public Filter(Filter... value) { + this(null, null, value); + } + + public static Filter as(String key, Relation relation, Object... value) { + return new Filter(key, relation, value); + } + + public static Filter as(String key, Object... value) { + return new Filter(key, value); + } + + public static Filter as(Filter... value) { + return new Filter(value); + } + + public boolean isEqual() { + return relation == Relation.EQUAL; + } + + public boolean isLike() { + return relation == Relation.LIKE; + } + + public boolean isBetween() { + return relation == Relation.BETWEEN; + } + + public boolean isIn() { + return relation == Relation.IN; + } + + public boolean isOr() { + return value != null && value.length > 0 && value[0] instanceof Filter; + } + + public boolean isGreaterThan() { + return relation == Relation.GREATER_THAN; + } + + public boolean isLessThan() { + return relation == Relation.LESS_THAN; + } + + public boolean isGreaterThanEqual() { + return relation == Relation.GREATER_THAN_EQUAL; + } + + public boolean isLessThanEqual() { + return relation == Relation.LESS_THAN_EQUAL; + } + + public Object getFilterValue(Class clazz) { + Object val = value[0]; + if (val instanceof String) { + if (clazz == Integer.class) { + return Integer.parseInt((String) val); + } + if (clazz == Long.class) { + return Long.parseLong((String) val); + } + if (clazz == Double.class) { + return Double.parseDouble((String) val); + } + if (clazz == Float.class) { + return Float.parseFloat((String) val); + } + } + return value[0]; + } + +} diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java index 312a8dbe..65257362 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java @@ -1,17 +1,12 @@ package com.codingapi.springboot.framework.dto.request; +import javax.servlet.http.HttpServletRequest; import lombok.Getter; -import lombok.Setter; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import org.springframework.util.StringUtils; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; -import javax.servlet.http.HttpServletRequest; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; import java.util.Optional; public class PageRequest extends org.springframework.data.domain.PageRequest { @@ -21,7 +16,7 @@ public class PageRequest extends org.springframework.data.domain.PageRequest { private int pageSize; @Getter - private final Map filters = new HashMap<>(); + private final RequestFilter requestFilter = new RequestFilter(); @Getter private HttpServletRequest servletRequest; @@ -37,23 +32,12 @@ public PageRequest(int current, int pageSize, Sort sort) { try { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); this.servletRequest = attributes.getRequest(); - this.syncParameter(); + requestFilter.syncParameter(servletRequest); } catch (Exception e) { } } - private void syncParameter() { - Enumeration enumeration = servletRequest.getParameterNames(); - while (enumeration.hasMoreElements()) { - String key = enumeration.nextElement(); - String value = servletRequest.getParameter(key); - if (StringUtils.hasText(value)) { - addFilter(key, value); - } - } - } - public PageRequest() { this(0, 20, Sort.unsorted()); } @@ -80,43 +64,25 @@ public int getIntParameter(String key, int defaultValue) { return result == null ? defaultValue : Integer.parseInt(result); } - public String getStringFilter(String key) { - Filter filter = (Filter) filters.get(key); - if (filter != null) { - return (String) filter.getValue()[0]; - } - return null; + return requestFilter.getStringFilter(key); } public String getStringFilter(String key, String defaultValue) { - String value = getStringFilter(key); - if (!StringUtils.hasText(value)) { - return defaultValue; - } - return value; + return requestFilter.getStringFilter(key, defaultValue); } public int getIntFilter(String key) { - Filter filter = (Filter) filters.get(key); - if (filter != null) { - String value = (String) filter.getValue()[0]; - if (StringUtils.hasText(value)) { - return Integer.parseInt(value); - } - return 0; - } - return 0; + return requestFilter.getIntFilter(key); } public int getIntFilter(String key, int defaultValue) { - int value = getIntFilter(key); - if (value == 0) { - return defaultValue; - } - return value; + return requestFilter.getIntFilter(key, defaultValue); } + public boolean hasFilter() { + return requestFilter.hasFilter(); + } @Override public int getPageSize() { @@ -196,114 +162,21 @@ public void addSort(Sort sort) { } } - public PageRequest addFilter(String key, FilterRelation relation, Object... value) { - putFilter(key, relation, value); + public PageRequest addFilter(String key, Relation relation, Object... value) { + requestFilter.addFilter(key, relation, value); return this; } public PageRequest addFilter(String key, Object... value) { - return this.addFilter(key, FilterRelation.EUQAL, value); - } - - public PageRequest addOrFilters(Object... value) { - // 检查value数组是否为偶数长度,因为我们需要成对处理它 - if (value.length % 2 != 0) { - throw new IllegalArgumentException("Invalid number of elements in value array."); - } - - return this.addFilter("OR", FilterRelation.OR, value); - } - - public boolean hasFilter() { - return !this.filters.isEmpty(); - } - - @Setter - @Getter - public static class Filter { - private String key; - private Object[] value; - - private FilterRelation relation; - - public boolean isEqual() { - return relation == FilterRelation.EUQAL; - } - - public boolean isLike() { - return relation == FilterRelation.LIKE; - } - - public boolean isBetween() { - return relation == FilterRelation.BETWEEN; - } - - public boolean isIn() { - return relation == FilterRelation.IN; - } - - public boolean isOr() { - return relation == FilterRelation.OR; - } - - public boolean isGreaterThan() { - return relation == FilterRelation.GREATER_THAN; - } - - public boolean isLessThan() { - return relation == FilterRelation.LESS_THAN; - } - - public boolean isGreaterThanEqual() { - return relation == FilterRelation.GREATER_THAN_EQUAL; - } - - public boolean isLessThanEqual() { - return relation == FilterRelation.LESS_THAN_EQUAL; - } - - public Object getFilterValue(Class clazz) { - Object val = value[0]; - if (val instanceof String) { - if (clazz == Integer.class) { - return Integer.parseInt((String) val); - } - if (clazz == Long.class) { - return Long.parseLong((String) val); - } - if (clazz == Double.class) { - return Double.parseDouble((String) val); - } - if (clazz == Float.class) { - return Float.parseFloat((String) val); - } - } - return value[0]; - } - - } - - private void putFilter(String key, FilterRelation relation, Object... val) { - Filter filter = new Filter(); - filter.setKey(key); - filter.setValue(val); - filter.setRelation(relation); - this.filters.put(key, filter); + requestFilter.addFilter(key, value); + return this; } - public enum FilterRelation { - EUQAL, - LIKE, - BETWEEN, - IN, - GREATER_THAN, - LESS_THAN, - GREATER_THAN_EQUAL, - LESS_THAN_EQUAL, - OR, + public PageRequest orFilters(Filter... filters) { + requestFilter.orFilters(filters); + return this; } - public static PageRequest of(int page, int size) { return new PageRequest(page, size, Sort.unsorted()); } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Relation.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Relation.java new file mode 100644 index 00000000..a5b3eaa0 --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Relation.java @@ -0,0 +1,13 @@ +package com.codingapi.springboot.framework.dto.request; + +public enum Relation { + + EQUAL, + LIKE, + BETWEEN, + IN, + GREATER_THAN, + LESS_THAN, + GREATER_THAN_EQUAL, + LESS_THAN_EQUAL, +} diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java new file mode 100644 index 00000000..972b8823 --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java @@ -0,0 +1,95 @@ +package com.codingapi.springboot.framework.dto.request; + + +import javax.servlet.http.HttpServletRequest; +import org.springframework.util.StringUtils; + +import java.util.*; + +public class RequestFilter { + + private final Map filterMap = new HashMap<>(); + private final List filterList = new ArrayList<>(); + + public RequestFilter addFilter(String key, Relation relation, Object... value) { + this.pushFilter(new Filter(key, relation, value)); + return this; + } + + public RequestFilter addFilter(String key, Object... value) { + this.pushFilter(new Filter(key, value)); + return this; + } + + public RequestFilter orFilters(Filter... value) { + this.pushFilter(new Filter(value)); + return this; + } + + public List getFilters() { + return filterList; + } + + public void pushFilter(Filter filter) { + filterList.add(filter); + filterMap.put(filter.getKey(), filter); + } + + public void syncParameter(HttpServletRequest servletRequest) { + Enumeration enumeration = servletRequest.getParameterNames(); + while (enumeration.hasMoreElements()) { + String key = enumeration.nextElement(); + String value = servletRequest.getParameter(key); + if (StringUtils.hasText(value)) { + addFilter(key, value); + } + } + } + + + public String getStringFilter(String key) { + Filter filter = filterMap.get(key); + if (filter != null) { + return (String) filter.getValue()[0]; + } + return null; + } + + public String getStringFilter(String key, String defaultValue) { + String value = getStringFilter(key); + if (!StringUtils.hasText(value)) { + return defaultValue; + } + return value; + } + + public int getIntFilter(String key) { + Filter filter = filterMap.get(key); + if (filter != null) { + String value = (String) filter.getValue()[0]; + if (StringUtils.hasText(value)) { + return Integer.parseInt(value); + } + return 0; + } + return 0; + } + + public int getIntFilter(String key, int defaultValue) { + int value = getIntFilter(key); + if (value == 0) { + return defaultValue; + } + return value; + } + + + public boolean hasFilter() { + return !this.filterMap.isEmpty(); + } + + + public Filter getFilter(String name) { + return this.filterMap.get(name); + } +} From f2777e159df0d3a92671b2c89aac51e966da5b1f Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Sat, 28 Oct 2023 00:31:23 +0800 Subject: [PATCH 009/101] fix Filter --- .../codingapi/springboot/fast/query/QueryRequest.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java index ff794568..0575c991 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java @@ -4,13 +4,13 @@ import com.codingapi.springboot.framework.dto.request.Filter; import com.codingapi.springboot.framework.dto.request.PageRequest; import com.codingapi.springboot.framework.dto.request.RequestFilter; +import org.springframework.beans.BeanUtils; +import org.springframework.data.domain.Example; + import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.Order; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; -import org.springframework.beans.BeanUtils; -import org.springframework.data.domain.Example; - import java.beans.PropertyDescriptor; import java.util.ArrayList; import java.util.Date; @@ -169,7 +169,10 @@ private Predicate toPredicate(Filter filter, CriteriaBuilder criteriaBuilder Filter[] orFilters = (Filter[]) filter.getValue(); List orPredicates = new ArrayList<>(); for (Filter orFilter : orFilters) { - orPredicates.add(toPredicate(orFilter, criteriaBuilder, root, properties)); + Predicate predicate = toPredicate(orFilter, criteriaBuilder, root, properties); + if (predicate != null) { + orPredicates.add(predicate); + } } return criteriaBuilder.or(orPredicates.toArray(new Predicate[0])); } From 335736268c9a285c4d564c695b02ee6438ff70c3 Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Sat, 28 Oct 2023 01:13:59 +0800 Subject: [PATCH 010/101] fix Filter --- .../springboot/fast/query/QueryRequest.java | 16 +++++++++++-- .../springboot/fast/DemoRepositoryTest.java | 23 ++++++++++++++++++- .../framework/dto/request/Filter.java | 23 ++++++++++++++----- .../framework/dto/request/PageRequest.java | 5 ++++ .../framework/dto/request/RequestFilter.java | 9 ++++++-- 5 files changed, 65 insertions(+), 11 deletions(-) diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java index 0575c991..bc906675 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java @@ -77,7 +77,7 @@ public List getOrder(Root root, CriteriaBuilder criteriaBuilder) { private Predicate toPredicate(Filter filter, CriteriaBuilder criteriaBuilder, Root root, List properties) { String key = filter.getKey(); - if (filter.isOr() || properties.contains(key)) { + if (filter.isAddFilters() || filter.isOrFilters() || properties.contains(key)) { if (filter.isEqual()) { return criteriaBuilder.equal(root.get(key), filter.getValue()[0]); @@ -165,7 +165,7 @@ private Predicate toPredicate(Filter filter, CriteriaBuilder criteriaBuilder return in; } - if (filter.isOr()) { + if (filter.isOrFilters()) { Filter[] orFilters = (Filter[]) filter.getValue(); List orPredicates = new ArrayList<>(); for (Filter orFilter : orFilters) { @@ -176,6 +176,18 @@ private Predicate toPredicate(Filter filter, CriteriaBuilder criteriaBuilder } return criteriaBuilder.or(orPredicates.toArray(new Predicate[0])); } + + if (filter.isAddFilters()) { + Filter[] orFilters = (Filter[]) filter.getValue(); + List addPredicates = new ArrayList<>(); + for (Filter orFilter : orFilters) { + Predicate predicate = toPredicate(orFilter, criteriaBuilder, root, properties); + if (predicate != null) { + addPredicates.add(predicate); + } + } + return criteriaBuilder.and(addPredicates.toArray(new Predicate[0])); + } } return null; } diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java index 2cf69b91..3296f8b8 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java @@ -77,7 +77,7 @@ void pageRequest() { } @Test - void customSearchOr() { + void customSearchOrFilters() { demoRepository.deleteAll(); Demo demo1 = new Demo(); demo1.setName("123"); @@ -97,6 +97,27 @@ void customSearchOr() { assertEquals(2, page.getTotalElements()); } + @Test + void customSearchAddFilters() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demo2.setName("456"); + demoRepository.save(demo2); + + PageRequest request = new PageRequest(); + request.setCurrent(1); + request.setPageSize(10); + +// request.addFilters(Filter.as("id", Relation.IN, 1),Filter.as("id", Relation.IN, 2),Filter.as("id", Relation.IN, 3),Filter.as("id", Relation.IN, 4)); + request.orFilters(Filter.as("name", Relation.LIKE, "%2%"),Filter.and(Filter.as("id", Relation.IN, 1),Filter.as("id", Relation.IN, 2))); + Page page = demoRepository.pageRequest(request); + assertEquals(2, page.getTotalElements()); + } + @Test void dynamicListQuery() { diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java index eb143b60..f6ea8da7 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java @@ -8,6 +8,9 @@ @Getter public class Filter { + public static final String FILTER_OR_KEY = "FILTER_OR_KEY"; + public static final String FILTER_ADD_KEY = "FILTER_ADD_KEY"; + private String key; private Object[] value; private Relation relation; @@ -22,8 +25,8 @@ public Filter(String key, Object... value) { this(key, Relation.EQUAL, value); } - public Filter(Filter... value) { - this(null, null, value); + public Filter(String key, Filter... value) { + this(key, null, value); } public static Filter as(String key, Relation relation, Object... value) { @@ -34,8 +37,12 @@ public static Filter as(String key, Object... value) { return new Filter(key, value); } - public static Filter as(Filter... value) { - return new Filter(value); + public static Filter and(Filter... value) { + return new Filter(FILTER_ADD_KEY, value); + } + + public static Filter or(Filter... value) { + return new Filter(FILTER_OR_KEY, value); } public boolean isEqual() { @@ -54,8 +61,12 @@ public boolean isIn() { return relation == Relation.IN; } - public boolean isOr() { - return value != null && value.length > 0 && value[0] instanceof Filter; + public boolean isOrFilters() { + return FILTER_OR_KEY.equals(key); + } + + public boolean isAddFilters() { + return FILTER_ADD_KEY.equals(key); } public boolean isGreaterThan() { diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java index 65257362..923903d8 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java @@ -172,6 +172,11 @@ public PageRequest addFilter(String key, Object... value) { return this; } + public PageRequest addFilters(Filter... filters) { + requestFilter.addFilters(filters); + return this; + } + public PageRequest orFilters(Filter... filters) { requestFilter.orFilters(filters); return this; diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java index 972b8823..8fbc2b4b 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java @@ -1,9 +1,9 @@ package com.codingapi.springboot.framework.dto.request; -import javax.servlet.http.HttpServletRequest; import org.springframework.util.StringUtils; +import javax.servlet.http.HttpServletRequest; import java.util.*; public class RequestFilter { @@ -21,8 +21,13 @@ public RequestFilter addFilter(String key, Object... value) { return this; } + public RequestFilter addFilters(Filter... value) { + this.pushFilter(new Filter(Filter.FILTER_ADD_KEY, value)); + return this; + } + public RequestFilter orFilters(Filter... value) { - this.pushFilter(new Filter(value)); + this.pushFilter(new Filter(Filter.FILTER_OR_KEY, value)); return this; } From 268595d731a9bc8dd6def44dbdf823032df89473 Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Sat, 28 Oct 2023 01:47:47 +0800 Subject: [PATCH 011/101] fix Filter --- .../com/codingapi/springboot/fast/DemoRepositoryTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java index 3296f8b8..ac821ffa 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java @@ -91,10 +91,10 @@ void customSearchOrFilters() { request.setCurrent(1); request.setPageSize(10); - request.orFilters(Filter.as("name", Relation.LIKE, "%2%"), Filter.as("id", Relation.IN, 1, 2, 3)); + request.orFilters(Filter.and(Filter.as("name", Relation.LIKE, "%2%"),Filter.as("name", Relation.LIKE, "%1%")), Filter.and(Filter.as("id", Relation.IN, 1),Filter.as("id", Relation.IN, 2))); Page page = demoRepository.pageRequest(request); - assertEquals(2, page.getTotalElements()); + assertEquals(1, page.getTotalElements()); } @Test @@ -113,9 +113,9 @@ void customSearchAddFilters() { request.setPageSize(10); // request.addFilters(Filter.as("id", Relation.IN, 1),Filter.as("id", Relation.IN, 2),Filter.as("id", Relation.IN, 3),Filter.as("id", Relation.IN, 4)); - request.orFilters(Filter.as("name", Relation.LIKE, "%2%"),Filter.and(Filter.as("id", Relation.IN, 1),Filter.as("id", Relation.IN, 2))); + request.addFilters(Filter.as("name", Relation.LIKE, "%2%"), Filter.and(Filter.as("id", Relation.IN, 1), Filter.as("id", Relation.IN, 2))); Page page = demoRepository.pageRequest(request); - assertEquals(2, page.getTotalElements()); + assertEquals(0, page.getTotalElements()); } From 65908bd69789ed462f7dc377c9c3b9dd43203aa8 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sat, 28 Oct 2023 12:18:43 +0800 Subject: [PATCH 012/101] update 2.7.2 --- .../com/codingapi/springboot/fast/query/QueryRequest.java | 4 ++-- .../com/codingapi/springboot/fast/DemoRepositoryTest.java | 2 +- .../springboot/framework/dto/request/Filter.java | 8 ++++---- .../springboot/framework/dto/request/PageRequest.java | 4 ++-- .../springboot/framework/dto/request/RequestFilter.java | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java index bc906675..686ac1a8 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java @@ -77,7 +77,7 @@ public List getOrder(Root root, CriteriaBuilder criteriaBuilder) { private Predicate toPredicate(Filter filter, CriteriaBuilder criteriaBuilder, Root root, List properties) { String key = filter.getKey(); - if (filter.isAddFilters() || filter.isOrFilters() || properties.contains(key)) { + if (filter.isAndFilters() || filter.isOrFilters() || properties.contains(key)) { if (filter.isEqual()) { return criteriaBuilder.equal(root.get(key), filter.getValue()[0]); @@ -177,7 +177,7 @@ private Predicate toPredicate(Filter filter, CriteriaBuilder criteriaBuilder return criteriaBuilder.or(orPredicates.toArray(new Predicate[0])); } - if (filter.isAddFilters()) { + if (filter.isAndFilters()) { Filter[] orFilters = (Filter[]) filter.getValue(); List addPredicates = new ArrayList<>(); for (Filter orFilter : orFilters) { diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java index ac821ffa..f79cd5f3 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java @@ -113,7 +113,7 @@ void customSearchAddFilters() { request.setPageSize(10); // request.addFilters(Filter.as("id", Relation.IN, 1),Filter.as("id", Relation.IN, 2),Filter.as("id", Relation.IN, 3),Filter.as("id", Relation.IN, 4)); - request.addFilters(Filter.as("name", Relation.LIKE, "%2%"), Filter.and(Filter.as("id", Relation.IN, 1), Filter.as("id", Relation.IN, 2))); + request.andFilters(Filter.as("name", Relation.LIKE, "%2%"), Filter.and(Filter.as("id", Relation.IN, 1), Filter.as("id", Relation.IN, 2))); Page page = demoRepository.pageRequest(request); assertEquals(0, page.getTotalElements()); } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java index f6ea8da7..0acb1477 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java @@ -9,7 +9,7 @@ public class Filter { public static final String FILTER_OR_KEY = "FILTER_OR_KEY"; - public static final String FILTER_ADD_KEY = "FILTER_ADD_KEY"; + public static final String FILTER_AND_KEY = "FILTER_AND_KEY"; private String key; private Object[] value; @@ -38,7 +38,7 @@ public static Filter as(String key, Object... value) { } public static Filter and(Filter... value) { - return new Filter(FILTER_ADD_KEY, value); + return new Filter(FILTER_AND_KEY, value); } public static Filter or(Filter... value) { @@ -65,8 +65,8 @@ public boolean isOrFilters() { return FILTER_OR_KEY.equals(key); } - public boolean isAddFilters() { - return FILTER_ADD_KEY.equals(key); + public boolean isAndFilters() { + return FILTER_AND_KEY.equals(key); } public boolean isGreaterThan() { diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java index 923903d8..7c0eb2df 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java @@ -172,8 +172,8 @@ public PageRequest addFilter(String key, Object... value) { return this; } - public PageRequest addFilters(Filter... filters) { - requestFilter.addFilters(filters); + public PageRequest andFilters(Filter... filters) { + requestFilter.andFilters(filters); return this; } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java index 8fbc2b4b..12e6d6ac 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java @@ -21,8 +21,8 @@ public RequestFilter addFilter(String key, Object... value) { return this; } - public RequestFilter addFilters(Filter... value) { - this.pushFilter(new Filter(Filter.FILTER_ADD_KEY, value)); + public RequestFilter andFilters(Filter... value) { + this.pushFilter(new Filter(Filter.FILTER_AND_KEY, value)); return this; } From 1bd5c1fefde7f1b150da4fa2b9b3ff398263bdb2 Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Sat, 28 Oct 2023 13:24:58 +0800 Subject: [PATCH 013/101] add wiki --- docs/wiki/springboot-starter-data-fast.md | 29 +++++++++++++++++-- docs/wiki/springboot-starter-security-jwt.md | 20 +++++++++---- .../springboot/fast/DemoRepositoryTest.java | 5 ++-- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/docs/wiki/springboot-starter-data-fast.md b/docs/wiki/springboot-starter-data-fast.md index 154795dc..cd631789 100644 --- a/docs/wiki/springboot-starter-data-fast.md +++ b/docs/wiki/springboot-starter-data-fast.md @@ -64,7 +64,7 @@ public interface DemoRepository extends FastRepository { ``` 动态FastRepository的能力展示 -```java +``` // 重写findAll,通过Example查询 @Test @@ -90,7 +90,7 @@ public interface DemoRepository extends FastRepository { // pageRequest 自定义条件查询 @Test - void pageRequest() { + void pageRequest1() { demoRepository.deleteAll(); Demo demo1 = new Demo(); demo1.setName("123"); @@ -104,10 +104,33 @@ public interface DemoRepository extends FastRepository { request.setCurrent(1); request.setPageSize(10); request.addFilter("name", PageRequest.FilterRelation.LIKE, "%2%"); + //sql: select demo0_.id as id1_0_, demo0_.name as name2_0_, demo0_.sort as sort3_0_ from t_demo demo0_ where demo0_.name like ? limit ? Page page = demoRepository.pageRequest(request); assertEquals(1, page.getTotalElements()); - } + } + + // pageRequest 自定义条件查询 + @Test + void pageRequest2() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demo2.setName("456"); + demoRepository.save(demo2); + + PageRequest request = new PageRequest(); + request.setCurrent(1); + request.setPageSize(10); + request.orFilters(Filter.as("name","123"),Filter.as("name","456")); + //sql: select demo0_.id as id1_0_, demo0_.name as name2_0_, demo0_.sort as sort3_0_ from t_demo demo0_ where demo0_.name=? or demo0_.name=? limit ? + + Page page = demoRepository.pageRequest(request); + assertEquals(1, page.getTotalElements()); + } // 动态sql的List查询 diff --git a/docs/wiki/springboot-starter-security-jwt.md b/docs/wiki/springboot-starter-security-jwt.md index dd984683..d7c71b5b 100644 --- a/docs/wiki/springboot-starter-security-jwt.md +++ b/docs/wiki/springboot-starter-security-jwt.md @@ -51,14 +51,22 @@ security默认的账户密码为admin/admin,可以通过重写UserDetailsServi 也可以通过数据库账户获取账户数据,请自己实现UserDetailsService接口 ## 登录拦截 -可以通过重写SecurityLoginHandler来实现自定义登录拦截 +可以通过重写SecurityLoginHandler来实现自定义登录拦截,preHandle登录前的拦截处理,postHandle登录后的拦截处理 ```java @Bean - public SecurityLoginHandler securityLoginHandler(){ - return (request, response, handler) -> { - //TODO 自定义登录拦截 - }; - } + public SecurityLoginHandler securityLoginHandler() { + return new SecurityLoginHandler() { + @Override + public void preHandle(HttpServletRequest request, HttpServletResponse response, LoginRequest handler) throws Exception { + + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, LoginRequest handler, Token token) { + + } + }; +} ``` ## 获取当前用户 diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java index f79cd5f3..bb00452f 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java @@ -90,11 +90,10 @@ void customSearchOrFilters() { PageRequest request = new PageRequest(); request.setCurrent(1); request.setPageSize(10); - - request.orFilters(Filter.and(Filter.as("name", Relation.LIKE, "%2%"),Filter.as("name", Relation.LIKE, "%1%")), Filter.and(Filter.as("id", Relation.IN, 1),Filter.as("id", Relation.IN, 2))); + request.orFilters(Filter.as("name","123"),Filter.as("name","456")); Page page = demoRepository.pageRequest(request); - assertEquals(1, page.getTotalElements()); + assertEquals(2, page.getTotalElements()); } @Test From 6a796c854be4edb04c46e78aa89f41b24deb4a5c Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Sat, 28 Oct 2023 13:36:26 +0800 Subject: [PATCH 014/101] =?UTF-8?q?add=20=E9=A1=B9=E7=9B=AE=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 149c9aa5..a3ddf406 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,11 @@ 本框架基于springboot为提供领域驱动设计与事件风暴开发落地,提供的范式开源框架。 +## Project Version | 项目版本说明 + +v.2.x 为springboot 2.x版本,使用jdk8版本 +v.3.x 为springboot 3.x版本,使用jdk17版本 + ## Project Modules Description | 项目模块介绍 * springboot-starter | Springboot领域驱动框架 From 380064909c5a554878c4915bdc87160e246beb1b Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Wed, 8 Nov 2023 17:45:20 +0800 Subject: [PATCH 015/101] #29 fix addFilter --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-id-generator/pom.xml | 2 +- springboot-starter-security-jwt/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../springboot/framework/dto/request/RequestFilter.java | 1 + 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index c03da97d..86d6d4bc 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.7.2 + 2.7.3 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 85efb421..29896644 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.2 + 2.7.3 4.0.0 diff --git a/springboot-starter-id-generator/pom.xml b/springboot-starter-id-generator/pom.xml index 3f3a6a34..8398f703 100644 --- a/springboot-starter-id-generator/pom.xml +++ b/springboot-starter-id-generator/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.2 + 2.7.3 4.0.0 diff --git a/springboot-starter-security-jwt/pom.xml b/springboot-starter-security-jwt/pom.xml index 68c74e24..221e1f0b 100644 --- a/springboot-starter-security-jwt/pom.xml +++ b/springboot-starter-security-jwt/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.7.2 + 2.7.3 springboot-starter-security-jwt diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 74016e37..d82e08b1 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.7.2 + 2.7.3 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java index 12e6d6ac..5c4cf90e 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java @@ -36,6 +36,7 @@ public List getFilters() { } public void pushFilter(Filter filter) { + filterList.removeIf(item -> item.getKey().equals(filter.getKey())); filterList.add(filter); filterMap.put(filter.getKey(), filter); } From 2b3f970a9b147ad0c323e681a53f84f2e6a0b127 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Wed, 8 Nov 2023 21:37:21 +0800 Subject: [PATCH 016/101] fix #29 update 2.7.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 86d6d4bc..c00e92b4 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.16 + 2.7.17 From 8fdb7a1448a6819870b842e80a42fc943888ede0 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Wed, 8 Nov 2023 22:58:34 +0800 Subject: [PATCH 017/101] fix #29 update 2.7.4 --- pom.xml | 34 ++++++++++++++----- springboot-starter-data-fast/pom.xml | 2 +- .../fast/dynamic/DynamicRepository.java | 1 + springboot-starter-id-generator/pom.xml | 2 +- springboot-starter-security-jwt/pom.xml | 2 +- .../springboot/security/jwt/Jwt.java | 12 +++---- springboot-starter/pom.xml | 2 +- .../serializable/MapSerializable.java | 3 +- 8 files changed, 39 insertions(+), 19 deletions(-) diff --git a/pom.xml b/pom.xml index c00e92b4..0f2c8d2e 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.7.3 + 2.7.4 https://github.com/codingapi/springboot-framewrok springboot-parent @@ -27,18 +27,19 @@ 8 3.0.1 3.11.0 - 2.10.3 - 1.6.3 - 1.6 + 3.6.1 + 1.6.13 + 3.1.0 ${project.version} - 2.0.28 - 0.11.5 - 2.11.0 - 1.7 + 2.0.42 + 0.12.3 + 2.15.0 + 1.8.1 0.10.2 0.9.16 1.70 1.2.0 + 2.2 @@ -46,6 +47,17 @@ org.springframework.boot spring-boot-starter + + + org.yaml + snakeyaml + + + + + + org.yaml + snakeyaml @@ -155,6 +167,12 @@ ${commons-dbutils.version} + + org.yaml + snakeyaml + ${org.yaml.snakeyaml.version} + + diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 29896644..dcccfc34 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.3 + 2.7.4 4.0.0 diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicRepository.java index fffd31a4..cd50e728 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicRepository.java @@ -9,6 +9,7 @@ import java.util.List; @NoRepositoryBean +@SuppressWarnings("unchecked") public interface DynamicRepository extends JpaRepository { default Class getEntityClass() { diff --git a/springboot-starter-id-generator/pom.xml b/springboot-starter-id-generator/pom.xml index 8398f703..f228fb24 100644 --- a/springboot-starter-id-generator/pom.xml +++ b/springboot-starter-id-generator/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.3 + 2.7.4 4.0.0 diff --git a/springboot-starter-security-jwt/pom.xml b/springboot-starter-security-jwt/pom.xml index 221e1f0b..e1d89429 100644 --- a/springboot-starter-security-jwt/pom.xml +++ b/springboot-starter-security-jwt/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.7.3 + 2.7.4 springboot-starter-security-jwt diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/jwt/Jwt.java b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/jwt/Jwt.java index c1b7a2b8..75f93713 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/jwt/Jwt.java +++ b/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/jwt/Jwt.java @@ -7,13 +7,13 @@ import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; +import javax.crypto.SecretKey; import java.nio.charset.StandardCharsets; -import java.security.Key; import java.util.List; public class Jwt { - private final Key key; + private final SecretKey key; private final int jwtTime; private final int jwtRestTime; @@ -37,16 +37,16 @@ public Token create(String username, String iv, List authorities){ public Token create(String username, String iv,List authorities,String extra){ Token token = new Token(username, iv,extra, authorities, jwtTime, jwtRestTime); - String jwt = Jwts.builder().setSubject(token.toJson()).signWith(key).compact(); + String jwt = Jwts.builder().subject(token.toJson()).signWith(key).compact(); token.setToken(jwt); return token; } public Token parser(String sign) { try { - Jws jws = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(sign); + Jws jws = Jwts.parser().verifyWith(key).build().parseSignedClaims(sign); if (jws != null) { - String subject = jws.getBody().getSubject(); + String subject = jws.getPayload().getSubject(); return JSONObject.parseObject(subject, Token.class); } throw new LocaleMessageException("token.error", "token失效,请重新登录."); @@ -54,4 +54,4 @@ public Token parser(String sign) { throw new LocaleMessageException("token.error", exp.getMessage()); } } -} +} \ No newline at end of file diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index d82e08b1..ff00567b 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.7.3 + 2.7.4 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/serializable/MapSerializable.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/serializable/MapSerializable.java index e7f96743..2b40aae7 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/serializable/MapSerializable.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/serializable/MapSerializable.java @@ -1,6 +1,7 @@ package com.codingapi.springboot.framework.serializable; import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; import java.util.Map; @@ -11,6 +12,6 @@ public interface MapSerializable { default Map toMap() { - return (Map) JSONObject.toJSON(this); + return JSON.parseObject(JSONObject.toJSONString(this)); } } From a6c86d42dd9bfb11af6d08404c3876495715eeca Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Thu, 9 Nov 2023 08:42:27 +0800 Subject: [PATCH 018/101] update snakeyaml version 2.2 --- pom.xml | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index 0f2c8d2e..cecd1c03 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ 0.9.16 1.70 1.2.0 - 2.2 + 2.2 @@ -47,17 +47,6 @@ org.springframework.boot spring-boot-starter - - - org.yaml - snakeyaml - - - - - - org.yaml - snakeyaml @@ -167,11 +156,6 @@ ${commons-dbutils.version} - - org.yaml - snakeyaml - ${org.yaml.snakeyaml.version} - @@ -301,7 +285,6 @@ - From 01dcc416e5857921dd21e8233fe2588c76a963c4 Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Sat, 9 Dec 2023 15:37:55 +0800 Subject: [PATCH 019/101] fix RestParam bug --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-id-generator/pom.xml | 2 +- springboot-starter-security-jwt/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../framework/rest/param/RestParam.java | 16 ---------------- 6 files changed, 5 insertions(+), 21 deletions(-) diff --git a/pom.xml b/pom.xml index cecd1c03..b6772394 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.7.4 + 2.7.5 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index dcccfc34..973e347b 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.4 + 2.7.5 4.0.0 diff --git a/springboot-starter-id-generator/pom.xml b/springboot-starter-id-generator/pom.xml index f228fb24..2df5ea76 100644 --- a/springboot-starter-id-generator/pom.xml +++ b/springboot-starter-id-generator/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.4 + 2.7.5 4.0.0 diff --git a/springboot-starter-security-jwt/pom.xml b/springboot-starter-security-jwt/pom.xml index e1d89429..97b1705d 100644 --- a/springboot-starter-security-jwt/pom.xml +++ b/springboot-starter-security-jwt/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.7.4 + 2.7.5 springboot-starter-security-jwt diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index ff00567b..85bb6c14 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.7.4 + 2.7.5 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/rest/param/RestParam.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/rest/param/RestParam.java index 7ad699a5..465d0b0f 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/rest/param/RestParam.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/rest/param/RestParam.java @@ -1,6 +1,5 @@ package com.codingapi.springboot.framework.rest.param; -import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import lombok.SneakyThrows; import org.springframework.util.LinkedMultiValueMap; @@ -35,21 +34,6 @@ private static void fetch(JSONObject object, RestParam builder) { Object value = object.getObject(key, Object.class); if (value != null) { builder.add(key, value); - - if (value instanceof JSONObject) { - JSONObject jsonObject = (JSONObject) value; - fetch(jsonObject, builder); - } - - if (value instanceof JSONArray) { - JSONArray jsonArray = (JSONArray) value; - for (Object o : jsonArray) { - if (o instanceof JSONObject) { - JSONObject jsonObject = (JSONObject) o; - fetch(jsonObject, builder); - } - } - } } } } From c76ec004ecde730c3dd9ecc5981c4389fbc9991f Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Tue, 2 Jan 2024 14:37:59 +0800 Subject: [PATCH 020/101] update 2.7.6 --- docs/wiki/springboot-starter-data-fast.md | 158 ++++++++++-------- docs/wiki/springboot-starter-id-generator.md | 34 ---- pom.xml | 32 +++- springboot-starter-data-fast/pom.xml | 28 +++- .../fast/DataFastConfiguration.java | 26 +-- .../fast/annotation/FastController.java | 11 -- .../fast/annotation/FastMapping.java | 38 ----- .../fast/dynamic/DynamicConfiguration.java | 21 --- .../fast/dynamic/DynamicQueryContext.java | 23 --- .../dynamic/DynamicQueryContextRegister.java | 16 -- .../exception/FastMappingErrorException.java | 8 - .../springboot/fast/executor/JpaExecutor.java | 39 ----- .../springboot/fast/executor/JpaQuery.java | 114 ------------- .../fast/executor/MvcMethodInterceptor.java | 39 ----- .../springboot/fast/jdbc/JdbcQuery.java | 71 ++++++++ .../fast/jdbc/JdbcQueryConfiguration.java | 22 +++ .../fast/jdbc/JdbcQueryContext.java | 23 +++ .../fast/jdbc/JdbcQueryContextRegister.java | 16 ++ .../DynamicQuery.java => jpa/JPAQuery.java} | 8 +- .../fast/jpa/JPAQueryConfiguration.java | 21 +++ .../fast/jpa/JPAQueryContextRegister.java | 16 ++ .../springboot/fast/jpa/JpaQueryContext.java | 23 +++ .../repository}/DynamicRepository.java | 13 +- .../repository}/FastRepository.java | 9 +- .../repository}/QueryRequest.java | 9 +- .../fast/manager/EntityManagerContent.java | 3 +- .../manager/EntityManagerInitializer.java | 3 +- ...ntMapping.java => MvcMappingRegister.java} | 24 ++- .../DataFastBeanDefinitionRegistrar.java | 33 ---- .../fast/registrar/MvcMappingRegistrar.java | 94 ----------- .../springboot/fast/script/ScriptMapping.java | 56 +++++++ .../fast/script/ScriptMappingRegister.java | 46 +++++ .../springboot/fast/script/ScriptMethod.java | 18 ++ .../springboot/fast/script/ScriptRequest.java | 46 +++++ .../springboot/fast/script/ScriptRuntime.java | 31 ++++ .../codingapi/springboot/fast/sort/ISort.java | 8 - .../springboot/fast/sort/SortRepository.java | 24 --- .../main/resources/META-INF/spring.factories | 4 +- ...ot.autoconfigure.AutoConfiguration.imports | 4 +- .../fast/DataFastApplicationTest.java | 34 ---- .../springboot/fast/DemoRepositoryTest.java | 50 ++---- .../springboot/fast/entity/Demo.java | 10 +- .../springboot/fast/query/FastDemoQuery.java | 23 --- .../fast/repository/DemoRepository.java | 5 +- .../fast/script/ScriptRuntimeTest.java | 16 ++ springboot-starter-id-generator/pom.xml | 37 ---- .../generator/AutoConfiguration.java | 27 --- .../springboot/generator/IdGenerate.java | 17 -- .../generator/IdGeneratorContext.java | 33 ---- .../springboot/generator/dao/DbHelper.java | 76 --------- .../springboot/generator/dao/IdKeyDao.java | 94 ----------- .../springboot/generator/domain/IdKey.java | 33 ---- .../properties/GeneratorJdbcProperties.java | 29 ---- .../generator/service/IdGenerateService.java | 24 --- .../main/resources/META-INF/spring.factories | 2 - ...ot.autoconfigure.AutoConfiguration.imports | 1 - .../generator/IdGenerateServiceTest.java | 21 --- .../generator/IdGeneratorApplication.java | 12 -- .../src/test/resources/application.properties | 1 - springboot-starter-security-jwt/pom.xml | 2 +- springboot-starter/pom.xml | 12 +- .../framework/dto/request/IdRequest.java | 33 ++++ .../framework/dto/request/PageRequest.java | 14 +- .../springboot/framework/em/IEnum.java | 6 + .../serializable/EnumSerializer.java | 16 ++ 65 files changed, 696 insertions(+), 1144 deletions(-) delete mode 100644 docs/wiki/springboot-starter-id-generator.md delete mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/annotation/FastController.java delete mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/annotation/FastMapping.java delete mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicConfiguration.java delete mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQueryContext.java delete mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQueryContextRegister.java delete mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/exception/FastMappingErrorException.java delete mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/JpaExecutor.java delete mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/JpaQuery.java delete mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/MvcMethodInterceptor.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQuery.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQueryConfiguration.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQueryContext.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQueryContextRegister.java rename springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/{dynamic/DynamicQuery.java => jpa/JPAQuery.java} (95%) create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQueryConfiguration.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQueryContextRegister.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JpaQueryContext.java rename springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/{dynamic => jpa/repository}/DynamicRepository.java (61%) rename springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/{query => jpa/repository}/FastRepository.java (94%) rename springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/{query => jpa/repository}/QueryRequest.java (99%) rename springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/mapping/{MvcEndpointMapping.java => MvcMappingRegister.java} (65%) delete mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/registrar/DataFastBeanDefinitionRegistrar.java delete mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/registrar/MvcMappingRegistrar.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptMapping.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptMappingRegister.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptMethod.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptRequest.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptRuntime.java delete mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/ISort.java delete mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/SortRepository.java delete mode 100644 springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DataFastApplicationTest.java delete mode 100644 springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/query/FastDemoQuery.java create mode 100644 springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/script/ScriptRuntimeTest.java delete mode 100644 springboot-starter-id-generator/pom.xml delete mode 100644 springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/AutoConfiguration.java delete mode 100644 springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/IdGenerate.java delete mode 100644 springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/IdGeneratorContext.java delete mode 100644 springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/dao/DbHelper.java delete mode 100644 springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/dao/IdKeyDao.java delete mode 100644 springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/domain/IdKey.java delete mode 100644 springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/properties/GeneratorJdbcProperties.java delete mode 100644 springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/service/IdGenerateService.java delete mode 100644 springboot-starter-id-generator/src/main/resources/META-INF/spring.factories delete mode 100644 springboot-starter-id-generator/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports delete mode 100644 springboot-starter-id-generator/src/test/java/com/codingapi/springboot/generator/IdGenerateServiceTest.java delete mode 100644 springboot-starter-id-generator/src/test/java/com/codingapi/springboot/generator/IdGeneratorApplication.java delete mode 100644 springboot-starter-id-generator/src/test/resources/application.properties create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/IdRequest.java create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/em/IEnum.java create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/serializable/EnumSerializer.java diff --git a/docs/wiki/springboot-starter-data-fast.md b/docs/wiki/springboot-starter-data-fast.md index cd631789..aa35baca 100644 --- a/docs/wiki/springboot-starter-data-fast.md +++ b/docs/wiki/springboot-starter-data-fast.md @@ -2,53 +2,8 @@ springboot-starter-data-fast 基于JPA的快速API能力服务 -## FastController 快速API能力服务 -```java -package com.codingapi.springboot.example.query; - -import com.codingapi.springboot.example.infrastructure.jpa.entity.DemoEntity; -import com.codingapi.springboot.example.infrastructure.jpa.pojo.PageSearch; -import com.codingapi.springboot.fast.annotation.FastController; -import com.codingapi.springboot.fast.annotation.FastMapping; -import com.codingapi.springboot.framework.dto.response.MultiResponse; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.RequestMethod; - -@FastController -public interface FastDemoApi { - - - @PreAuthorize(value = "hasRole('ROLE_ADMIN')") - @FastMapping( - method = RequestMethod.GET, - mapping = "/api/demo/findByName1", - value = "select d from DemoEntity d where name = :name", - countQuery = "select count(d) from DemoEntity d where name = :name") - MultiResponse findByName1(PageSearch query); - - - - @PreAuthorize(value = "hasRole('ROLE_USER')") - @FastMapping( - method = RequestMethod.GET, - mapping = "/api/demo/findByName2", - value = "select d from DemoEntity d where name = :name", - countQuery = "select count(d) from DemoEntity d where name = :name") - MultiResponse findByName2(PageSearch query); - -} - -``` -@FastController 用于标记当前接口为Fast接口 -@FastMapping 用于标记当前接口的映射关系 -mapping为接口映射路径,method为接口请求方法 -value为查询语句,countQuery为查询总数语句,query为查询参数,支持分页查询,排序查询,查询参数等等 -MultiResponse为返回结果 -@PreAuthorize(value = "hasRole('ROLE_USER')") 用于标记当前接口的权限,如果不需要权限可以不用添加 - ## FastRepository 的使用教程 - 继承FastRepository接口,实现自定义的接口,即可使用FastRepository的能力 ```java @@ -189,41 +144,98 @@ public interface DemoRepository extends FastRepository { } ``` -## SortRepository的使用教程 -```java +## ScriptMapping 教程 -public interface DemoRepository extends FastRepository, SortRepository { +通过动态添加mvc mapping实现查询功能. -} ``` +ScriptMapping scriptMapping = new ScriptMapping({mapinggUrl}, {mapinggMethod}, {mappingGrovvry}); +scriptMappingRegister.addMapping(scriptMapping); -SortRepository的能力展示 +``` +mapinggUrl 是mvc接口的地址 +mapinggMethod 是mvc接口的请求方式 +mappingGrovvry 是执行的查询脚本 -```java +脚本实例代码: +* 动态分页查询 +``` +// 获取name的请求参数 +var name = $request.getParameter("name",""); +var pageNumber = $request.getParameter("pageNumber",0); +var pageSize = $request.getParameter("pageSize",10); +// 创建分页对象 +var pageRequest = $request.pageRequest(pageNumber,pageSize); +// 动态组织sql +var sql = "select * from api_mapping where 1 =1 "; +var countSql = "select count(1) from api_mapping where 1 =1 "; +// 动态组织参数 +var params = []; +if(!"".equals(name)){ + sql += " and name = ? "; + countSql += " and name = ? "; + params.push(name); +} +sql += " limit ?,?"; +// 添加分页参数 +params.add(pageRequest.getOffset()); +params.add(pageRequest.getPageSize()); +// 执行分页查询 +return $jdbc.queryForPage(sql,countSql,pageRequest,params.toArray()); +``` +* 动态条件查询 +``` +// 获取name的请求参数 +var name = $request.getParameter("name",""); +// 动态组织sql +String sql = "select * from api_mapping where 1=1 "; +// 动态组织参数 +var params = []; +if(!"".equals(name)){ + sql += " and name = ? "; + params.add(name); +} +// 执行查询 +return $jdbc.queryForList(sql,params.toArray()); +``` - @Test - @Transactional - void pageSort() { - demoRepository.deleteAll(); - Demo demo1 = new Demo(); - demo1.setName("123"); - demoRepository.save(demo1); - - Demo demo2 = new Demo(); - demo2.setName("456"); - demoRepository.save(demo2); - - List ids = Arrays.asList(demo1.getId(), demo2.getId()); - System.out.println(ids); - demoRepository.pageSort(PageRequest.of(1, 10), ids); - - Demo newDemo1 = demoRepository.getReferenceById(demo1.getId()); - Demo newDemo2 = demoRepository.getReferenceById(demo2.getId()); - - assertEquals(newDemo2.getSort(), 1); - assertEquals(newDemo1.getSort(), 0); - } +脚本语法介绍: +* $request +``` +// 获取参数name的值,如果参数不存在,则返回默认值 +var name = $request.getParameter("name",""); +// 获取分页对象 +var pageRequest = $request.pageRequest(0,10); +// 获取分页对象的页码 +var pageNumber = pageRequest.getPageNumber(); +// 获取分页对象的每页记录数 +var pageSize = pageRequest.getPageSize(); +// 获取分页对象的偏移量 +var offset = pageRequest.getOffset(); +``` +* $jdbc +``` +// 查询jdbcSQL $jdbc.queryForList({sql},{params}) + +// 查询无条件的数据 +var res = $jdbc.queryForList("select * from api_mapping"); +// 查询有条件的数据 +var res = $jdbc.queryForList("select * from api_mapping where name = ?",name); +// 查询多条件的数据 +var res = $jdbc.queryForList("select * from api_mapping where name = ? and url = ?",name,url); + +// 分页查询 $jdbc.queryForPage({sql},{countSql},{pageRequest},{params}) +var res = $jdbc.queryForPage("select * from api_mapping where name = ? and url = ?", +"select count(1) from api_mapping where name = ? and url = ?",pageRequest,params.toArray()); +``` +* $jpa +``` +// 查询jpa $jpa.listQuery({clazz},{sql},{params}) +// 查询无条件的数据 +var res = $jpa.listQuery(com.example.entity.NodeEntity.class,"from NodeEntity"); +// 查询有条件的数据 +var res = $jpa.listQuery(com.example.entity.NodeEntity.class,"from NodeEntity where name = ?",name); ``` \ No newline at end of file diff --git a/docs/wiki/springboot-starter-id-generator.md b/docs/wiki/springboot-starter-id-generator.md deleted file mode 100644 index 27bb96a9..00000000 --- a/docs/wiki/springboot-starter-id-generator.md +++ /dev/null @@ -1,34 +0,0 @@ -springboot-starter-id-generator - -一个单服务下的id自增构造器,为了解决id由数据库生成的问题,如果是分布式服务,可以使用snowflake算法生成id,如果是单服务,可以使用这个构造器生成id - -```java - -import com.codingapi.springboot.generator.IdGenerate; -import lombok.Getter; - -/** - * @author lorne - * @since 1.0.0 - */ -public class Demo implements IdGenerate { - - @Getter - private Integer id; - - @Getter - private String name; - - public Demo(String name) { - this.id = generateIntId(); - this.name = name; - } - -} - -``` - -只需要实现IdGenerate接口即可,然后调用generateIntId()方法即可生成id。 - -IdGenerate接口提供了两个方法,generateIntId()和generateLongId(),分别用于生成int类型和long类型的id。 -也可以使用generateStringId()方法生成String类型的id。 \ No newline at end of file diff --git a/pom.xml b/pom.xml index b6772394..f41008e8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,13 +6,13 @@ org.springframework.boot spring-boot-starter-parent - 2.7.17 + 2.7.18 com.codingapi.springboot springboot-parent - 2.7.5 + 2.7.6 https://github.com/codingapi/springboot-framewrok springboot-parent @@ -35,11 +35,13 @@ 0.12.3 2.15.0 1.8.1 + 1.11.0 0.10.2 0.9.16 1.70 1.2.0 2.2 + 4.0.15 @@ -156,6 +158,29 @@ ${commons-dbutils.version} + + org.apache.commons + commons-text + ${commons-text.version} + + + + org.apache.groovy + groovy + ${apache-groovy.version} + + + + org.apache.groovy + groovy-json + ${apache-groovy.version} + + + + org.apache.groovy + groovy-xml + ${apache-groovy.version} + @@ -219,7 +244,6 @@ springboot-starter springboot-starter-security-jwt springboot-starter-data-fast - springboot-starter-id-generator @@ -231,7 +255,6 @@ springboot-starter springboot-starter-security-jwt springboot-starter-data-fast - springboot-starter-id-generator @@ -281,7 +304,6 @@ springboot-starter springboot-starter-security-jwt springboot-starter-data-fast - springboot-starter-id-generator diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 973e347b..b6e0468e 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.5 + 2.7.6 4.0.0 @@ -28,6 +28,12 @@ springboot-starter + + jakarta.servlet + jakarta.servlet-api + provided + + org.springframework.boot spring-boot-starter-data-jpa @@ -39,6 +45,26 @@ test + + org.apache.commons + commons-text + + + + org.apache.groovy + groovy + + + + org.apache.groovy + groovy-json + + + + org.apache.groovy + groovy-xml + + \ No newline at end of file diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/DataFastConfiguration.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/DataFastConfiguration.java index f977da31..2fb91bcd 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/DataFastConfiguration.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/DataFastConfiguration.java @@ -1,10 +1,8 @@ package com.codingapi.springboot.fast; -import com.codingapi.springboot.fast.executor.JpaExecutor; import com.codingapi.springboot.fast.manager.EntityManagerInitializer; -import com.codingapi.springboot.fast.mapping.MvcEndpointMapping; -import com.codingapi.springboot.fast.registrar.MvcMappingRegistrar; -import org.springframework.aop.Advisor; +import com.codingapi.springboot.fast.mapping.MvcMappingRegister; +import com.codingapi.springboot.fast.script.ScriptMappingRegister; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @@ -13,7 +11,6 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import javax.persistence.EntityManager; -import java.util.List; @Configuration @ConditionalOnClass(WebMvcConfigurer.class) @@ -22,28 +19,21 @@ public class DataFastConfiguration { @Bean @ConditionalOnMissingBean - public MvcEndpointMapping mvcEndpointMapping(RequestMappingHandlerMapping handlerMapping) { - return new MvcEndpointMapping(handlerMapping); + public MvcMappingRegister mvcMappingRegister(RequestMappingHandlerMapping handlerMapping) { + return new MvcMappingRegister(handlerMapping); } - @Bean(initMethod = "registerMvcMapping") - @ConditionalOnMissingBean - public MvcMappingRegistrar mappingRegistrar(MvcEndpointMapping mvcEndpointMapping, - JpaExecutor jpaExecutor, - List advisors) { - return new MvcMappingRegistrar(mvcEndpointMapping, jpaExecutor,advisors); - } @Bean @ConditionalOnMissingBean - public EntityManagerInitializer entityManagerInitializer(EntityManager entityManager){ + public EntityManagerInitializer entityManagerInitializer(EntityManager entityManager) { return new EntityManagerInitializer(entityManager); } + @Bean - @ConditionalOnMissingBean - public JpaExecutor jpaExecutor(EntityManager entityManager) { - return new JpaExecutor(entityManager); + public ScriptMappingRegister scriptMappingRegister(MvcMappingRegister mvcMappingRegister) { + return new ScriptMappingRegister(mvcMappingRegister); } } diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/annotation/FastController.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/annotation/FastController.java deleted file mode 100644 index e566cf7b..00000000 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/annotation/FastController.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.codingapi.springboot.fast.annotation; - -import java.lang.annotation.*; - -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface FastController { - - -} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/annotation/FastMapping.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/annotation/FastMapping.java deleted file mode 100644 index 1b1d8bb2..00000000 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/annotation/FastMapping.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.codingapi.springboot.fast.annotation; - - -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@ResponseBody -public @interface FastMapping { - - /** - * execute jpa hql - */ - String value() default ""; - - - /** - * execute jpa count hql - */ - String countQuery() default ""; - - /** - * mvc request method - */ - RequestMethod method() default RequestMethod.GET; - - /** - * mvc request url - */ - String mapping() default ""; - -} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicConfiguration.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicConfiguration.java deleted file mode 100644 index 1fe09262..00000000 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicConfiguration.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.codingapi.springboot.fast.dynamic; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import javax.persistence.EntityManager; - -@Configuration -public class DynamicConfiguration { - - @Bean - public DynamicQuery dynamicQuery(EntityManager entityManager){ - return new DynamicQuery(entityManager); - } - - @Bean - public DynamicQueryContextRegister dynamicQueryContextRegister(DynamicQuery dynamicQuery){ - return new DynamicQueryContextRegister(dynamicQuery); - } - -} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQueryContext.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQueryContext.java deleted file mode 100644 index 7e1e9230..00000000 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQueryContext.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.codingapi.springboot.fast.dynamic; - - -import lombok.Getter; - -public class DynamicQueryContext { - - @Getter - private static final DynamicQueryContext instance = new DynamicQueryContext(); - - private DynamicQueryContext() { - - } - - @Getter - private DynamicQuery dynamicQuery; - - void setDynamicQuery(DynamicQuery dynamicQuery) { - this.dynamicQuery = dynamicQuery; - } - - -} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQueryContextRegister.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQueryContextRegister.java deleted file mode 100644 index 4ed1cb8d..00000000 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQueryContextRegister.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.codingapi.springboot.fast.dynamic; - -import lombok.AllArgsConstructor; -import org.springframework.beans.factory.InitializingBean; - -@AllArgsConstructor -public class DynamicQueryContextRegister implements InitializingBean { - - private DynamicQuery dynamicQuery; - - @Override - public void afterPropertiesSet() throws Exception { - DynamicQueryContext.getInstance().setDynamicQuery(dynamicQuery); - } - -} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/exception/FastMappingErrorException.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/exception/FastMappingErrorException.java deleted file mode 100644 index ce41c99c..00000000 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/exception/FastMappingErrorException.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.codingapi.springboot.fast.exception; - -public class FastMappingErrorException extends Exception { - - public FastMappingErrorException(String message) { - super(message); - } -} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/JpaExecutor.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/JpaExecutor.java deleted file mode 100644 index a7795989..00000000 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/JpaExecutor.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.codingapi.springboot.fast.executor; - -import com.codingapi.springboot.framework.dto.response.MultiResponse; -import com.codingapi.springboot.framework.dto.response.SingleResponse; -import lombok.AllArgsConstructor; -import org.springframework.data.domain.Page; - -import javax.persistence.EntityManager; -import java.util.Collection; -import java.util.List; - -@AllArgsConstructor -public class JpaExecutor { - - private final EntityManager entityManager; - - public Object execute(String hql, String countHql, Object[] args, Class returnType) { - //only execute query sql - JpaQuery query = new JpaQuery(hql, countHql, args, entityManager); - - if (returnType.equals(SingleResponse.class)) { - List list = (List) query.getResultList(); - return list == null && list.size() > 0 ? SingleResponse.of(list.get(0)) : SingleResponse.empty(); - } - - if (returnType.equals(MultiResponse.class)) { - Object returnData = query.getResultList(); - if (Page.class.isAssignableFrom(returnData.getClass())) { - return MultiResponse.of((Page) returnData); - } - - if (Collection.class.isAssignableFrom(returnData.getClass())) { - return MultiResponse.of((Collection) returnData); - } - } - - return query.getResultList(); - } -} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/JpaQuery.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/JpaQuery.java deleted file mode 100644 index 0aa880f0..00000000 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/JpaQuery.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.codingapi.springboot.fast.executor; - -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.Pageable; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; - -import javax.persistence.EntityManager; -import javax.persistence.Parameter; -import javax.persistence.Query; -import java.lang.reflect.Field; -import java.util.Set; - -@Slf4j -public class JpaQuery { - private final Object[] args; - private final Query query; - private final String hql; - private final String countHql; - - private final EntityManager entityManager; - - public JpaQuery(String hql, String countHql, Object[] args, EntityManager entityManager) { - this.hql = hql; - this.countHql = countHql; - this.args = args; - this.entityManager = entityManager; - this.query = entityManager.createQuery(hql); - this.initParameter(query); - } - - /** - * init query parameter - */ - @SneakyThrows - private void initParameter(Query query) { - if (args != null && args.length > 0) { - Set> parameters = query.getParameters(); - for (Parameter parameter : parameters) { - Integer position = parameter.getPosition(); - if (position != null) { - query.setParameter(position, args[position - 1]); - } - if (StringUtils.hasText(parameter.getName())) { - String name = parameter.getName(); - Object obj = args[0]; - Field field = ReflectionUtils.findField(obj.getClass(), name); - if (field != null) { - field.setAccessible(true); - query.setParameter(name, field.get(obj)); - } - } - } - } - } - - /** - * is Page Request - */ - private boolean isPageable() { - if (args != null && args.length > 0 && StringUtils.hasText(countHql)) { - Object lastObj = args[args.length - 1]; - return lastObj instanceof Pageable; - } - return false; - } - - /** - * get PageRequest - */ - private Pageable getPageable() { - if (isPageable()) { - Object lastObj = args[args.length - 1]; - return (Pageable) lastObj; - } - return null; - } - - /** - * execute get list result data - */ - public Object getResultList() { - if (isPageable()) { - Pageable pageable = getPageable(); - query.setFirstResult((int) pageable.getOffset()); - query.setMaxResults(pageable.getPageSize()); - long total = getCount(); - return new PageImpl<>(query.getResultList(), pageable, total); - } - return query.getResultList(); - } - - - /** - * get sql execute data count - */ - private long getCount() { - Query countQuery = entityManager.createQuery(countHql); - initParameter(countQuery); - return (Long) countQuery.getSingleResult(); - } - - - /** - * get single result data - */ - public Object getSingleResult() { - return query.getSingleResult(); - } - - -} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/MvcMethodInterceptor.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/MvcMethodInterceptor.java deleted file mode 100644 index fba74734..00000000 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/executor/MvcMethodInterceptor.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.codingapi.springboot.fast.executor; - -import com.codingapi.springboot.fast.annotation.FastMapping; -import lombok.AllArgsConstructor; -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; - -import java.lang.reflect.Method; - -@AllArgsConstructor -public class MvcMethodInterceptor implements MethodInterceptor { - - private final JpaExecutor jpaExecutor; - - @Override - public Object invoke(MethodInvocation invocation) - throws Throwable { - Method method = invocation.getMethod(); - Object[] args = invocation.getArguments(); - - if (method.equals(Object.class.getMethod("equals", Object.class))) { - return false; - } - - if (method.equals(Object.class.getMethod("hashCode"))) { - return hashCode(); - } - - FastMapping fastMapping = method.getAnnotation(FastMapping.class); - if (fastMapping != null) { - Class returnType = method.getReturnType(); - return jpaExecutor.execute(fastMapping.value(), fastMapping.countQuery(), args, returnType); - } - // mvc mapping proxy can't execute return null. - return null; - } - - -} \ No newline at end of file diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQuery.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQuery.java new file mode 100644 index 00000000..423ba1ea --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQuery.java @@ -0,0 +1,71 @@ +package com.codingapi.springboot.fast.jdbc; + +import org.apache.commons.text.CaseUtils; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.jdbc.core.BeanPropertyRowMapper; +import org.springframework.jdbc.core.RowMapper; + +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class JdbcQuery { + + private final org.springframework.jdbc.core.JdbcTemplate jdbcTemplate; + + public JdbcQuery(org.springframework.jdbc.core.JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + private static class CamelCaseRowMapper implements RowMapper> { + + @Override + public Map mapRow(ResultSet rs, int rowNum) throws SQLException { + ResultSetMetaData metaData = rs.getMetaData(); + int columnCount = metaData.getColumnCount(); + Map map = new HashMap<>(columnCount); + for (int i = 1; i <= columnCount; i++) { + String columnName = metaData.getColumnLabel(i); + map.put(CaseUtils.toCamelCase(columnName, false), rs.getObject(i)); + } + return map; + } + } + + public List> queryForList(String sql, Object... params) { + return jdbcTemplate.query(sql, params, new CamelCaseRowMapper()); + } + + public List queryForList(String sql, Class clazz, Object... params) { + return jdbcTemplate.query(sql, params, new BeanPropertyRowMapper<>(clazz)); + } + + public Page queryForPage(String sql, String countSql, Class clazz, PageRequest pageRequest, Object... params) { + List list = jdbcTemplate.query(sql, params, new BeanPropertyRowMapper<>(clazz)); + long count = this.countQuery(countSql, params); + return new PageImpl<>(list, pageRequest, count); + } + + public Page> queryForPage(String sql, String countSql, PageRequest pageRequest, Object... params) { + List> list = jdbcTemplate.query(sql, params, new CamelCaseRowMapper()); + + long count = this.countQuery(countSql, params); + return new PageImpl<>(list, pageRequest, count); + } + + + private long countQuery(String sql, Object... params) { + int paramsLength = params.length; + int countSqlParamsLength = sql.split("\\?").length - 1; + Object[] newParams = new Object[countSqlParamsLength]; + if (paramsLength > countSqlParamsLength) { + System.arraycopy(params, 0, newParams, 0, countSqlParamsLength); + } + return jdbcTemplate.queryForObject(sql, newParams, Long.class); + } +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQueryConfiguration.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQueryConfiguration.java new file mode 100644 index 00000000..aa05bd84 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQueryConfiguration.java @@ -0,0 +1,22 @@ +package com.codingapi.springboot.fast.jdbc; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; + + +@Configuration +public class JdbcQueryConfiguration { + + @Bean + public JdbcQuery jdbcQuery(JdbcTemplate jdbcTemplate) { + return new JdbcQuery(jdbcTemplate); + } + + @Bean + public JdbcQueryContextRegister jdbcQueryContextRegister(JdbcQuery jdbcQuery){ + return new JdbcQueryContextRegister(jdbcQuery); + } + + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQueryContext.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQueryContext.java new file mode 100644 index 00000000..7aaa455e --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQueryContext.java @@ -0,0 +1,23 @@ +package com.codingapi.springboot.fast.jdbc; + + +import lombok.Getter; + +public class JdbcQueryContext { + + @Getter + private static final JdbcQueryContext instance = new JdbcQueryContext(); + + private JdbcQueryContext() { + + } + + @Getter + private JdbcQuery jdbcQuery; + + void setJdbcQuery(JdbcQuery jdbcQuery) { + this.jdbcQuery = jdbcQuery; + } + + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQueryContextRegister.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQueryContextRegister.java new file mode 100644 index 00000000..47ab903d --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQueryContextRegister.java @@ -0,0 +1,16 @@ +package com.codingapi.springboot.fast.jdbc; + +import lombok.AllArgsConstructor; +import org.springframework.beans.factory.InitializingBean; + +@AllArgsConstructor +public class JdbcQueryContextRegister implements InitializingBean { + + private JdbcQuery jdbcQuery; + + @Override + public void afterPropertiesSet() throws Exception { + JdbcQueryContext.getInstance().setJdbcQuery(jdbcQuery); + } + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQuery.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQuery.java similarity index 95% rename from springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQuery.java rename to springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQuery.java index 5727d337..00d64c2a 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicQuery.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQuery.java @@ -1,16 +1,16 @@ -package com.codingapi.springboot.fast.dynamic; +package com.codingapi.springboot.fast.jpa; +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; import lombok.AllArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; -import javax.persistence.EntityManager; -import javax.persistence.TypedQuery; import java.util.List; @AllArgsConstructor -public class DynamicQuery { +public class JPAQuery { private final EntityManager entityManager; diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQueryConfiguration.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQueryConfiguration.java new file mode 100644 index 00000000..598fe0c6 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQueryConfiguration.java @@ -0,0 +1,21 @@ +package com.codingapi.springboot.fast.jpa; + +import javax.persistence.EntityManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +@Configuration +public class JPAQueryConfiguration { + + @Bean + public JPAQuery dynamicQuery(EntityManager entityManager){ + return new JPAQuery(entityManager); + } + + @Bean + public JPAQueryContextRegister jpaQueryContextRegister(JPAQuery JPAQuery){ + return new JPAQueryContextRegister(JPAQuery); + } + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQueryContextRegister.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQueryContextRegister.java new file mode 100644 index 00000000..b6fc7198 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQueryContextRegister.java @@ -0,0 +1,16 @@ +package com.codingapi.springboot.fast.jpa; + +import lombok.AllArgsConstructor; +import org.springframework.beans.factory.InitializingBean; + +@AllArgsConstructor +public class JPAQueryContextRegister implements InitializingBean { + + private JPAQuery JPAQuery; + + @Override + public void afterPropertiesSet() throws Exception { + JpaQueryContext.getInstance().setJPAQuery(JPAQuery); + } + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JpaQueryContext.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JpaQueryContext.java new file mode 100644 index 00000000..e39b2d5c --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JpaQueryContext.java @@ -0,0 +1,23 @@ +package com.codingapi.springboot.fast.jpa; + + +import lombok.Getter; + +public class JpaQueryContext { + + @Getter + private static final JpaQueryContext instance = new JpaQueryContext(); + + private JpaQueryContext() { + + } + + @Getter + private JPAQuery JPAQuery; + + void setJPAQuery(JPAQuery JPAQuery) { + this.JPAQuery = JPAQuery; + } + + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRepository.java similarity index 61% rename from springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicRepository.java rename to springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRepository.java index cd50e728..186f45a1 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/dynamic/DynamicRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRepository.java @@ -1,5 +1,6 @@ -package com.codingapi.springboot.fast.dynamic; +package com.codingapi.springboot.fast.jpa.repository; +import com.codingapi.springboot.fast.jpa.JpaQueryContext; import org.springframework.core.ResolvableType; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -18,23 +19,23 @@ default Class getEntityClass() { } default List dynamicListQuery(String sql, Object... params) { - return (List) DynamicQueryContext.getInstance().getDynamicQuery().listQuery(getEntityClass(), sql, params); + return (List) JpaQueryContext.getInstance().getJPAQuery().listQuery(getEntityClass(), sql, params); } default List dynamicListQuery(Class clazz, String sql, Object... params) { - return (List) DynamicQueryContext.getInstance().getDynamicQuery().listQuery(clazz, sql, params); + return (List) JpaQueryContext.getInstance().getJPAQuery().listQuery(clazz, sql, params); } default Page dynamicPageQuery(String sql, String countSql, PageRequest request, Object... params) { - return (Page) DynamicQueryContext.getInstance().getDynamicQuery().pageQuery(getEntityClass(), sql, countSql, request, params); + return (Page) JpaQueryContext.getInstance().getJPAQuery().pageQuery(getEntityClass(), sql, countSql, request, params); } default Page dynamicPageQuery(String sql, PageRequest request, Object... params) { - return (Page) DynamicQueryContext.getInstance().getDynamicQuery().pageQuery(getEntityClass(), sql, "select count(1) " + sql, request, params); + return (Page) JpaQueryContext.getInstance().getJPAQuery().pageQuery(getEntityClass(), sql, "select count(1) " + sql, request, params); } default Page dynamicPageQuery(Class clazz, String sql, String countSql, PageRequest request, Object... params) { - return (Page) DynamicQueryContext.getInstance().getDynamicQuery().pageQuery(clazz, sql, countSql, request, params); + return (Page) JpaQueryContext.getInstance().getJPAQuery().pageQuery(clazz, sql, countSql, request, params); } } diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/FastRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java similarity index 94% rename from springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/FastRepository.java rename to springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java index c1a4374d..3e2f86bb 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/FastRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java @@ -1,7 +1,8 @@ -package com.codingapi.springboot.fast.query; +package com.codingapi.springboot.fast.jpa.repository; -import com.codingapi.springboot.fast.dynamic.DynamicRepository; import com.codingapi.springboot.framework.dto.request.PageRequest; +import javax.persistence.criteria.Order; +import javax.persistence.criteria.Predicate; import org.springframework.core.ResolvableType; import org.springframework.data.domain.Page; import org.springframework.data.jpa.domain.Specification; @@ -9,8 +10,6 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.NoRepositoryBean; -import javax.persistence.criteria.Order; -import javax.persistence.criteria.Predicate; import java.util.List; @NoRepositoryBean @@ -48,4 +47,4 @@ default Page pageRequest(PageRequest request) { return findAll((org.springframework.data.domain.PageRequest) request); } -} \ No newline at end of file +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/QueryRequest.java similarity index 99% rename from springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java rename to springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/QueryRequest.java index 686ac1a8..4cf15162 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/query/QueryRequest.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/QueryRequest.java @@ -1,16 +1,15 @@ -package com.codingapi.springboot.fast.query; - +package com.codingapi.springboot.fast.jpa.repository; import com.codingapi.springboot.framework.dto.request.Filter; import com.codingapi.springboot.framework.dto.request.PageRequest; import com.codingapi.springboot.framework.dto.request.RequestFilter; -import org.springframework.beans.BeanUtils; -import org.springframework.data.domain.Example; - import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.Order; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import org.springframework.beans.BeanUtils; +import org.springframework.data.domain.Example; + import java.beans.PropertyDescriptor; import java.util.ArrayList; import java.util.Date; diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/manager/EntityManagerContent.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/manager/EntityManagerContent.java index 96ab6ba2..3fd6ab67 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/manager/EntityManagerContent.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/manager/EntityManagerContent.java @@ -1,8 +1,7 @@ package com.codingapi.springboot.fast.manager; -import lombok.Getter; - import javax.persistence.EntityManager; +import lombok.Getter; public class EntityManagerContent { diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/manager/EntityManagerInitializer.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/manager/EntityManagerInitializer.java index a262fc20..e77dd8b8 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/manager/EntityManagerInitializer.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/manager/EntityManagerInitializer.java @@ -1,10 +1,9 @@ package com.codingapi.springboot.fast.manager; +import javax.persistence.EntityManager; import lombok.AllArgsConstructor; import org.springframework.beans.factory.InitializingBean; -import javax.persistence.EntityManager; - @AllArgsConstructor public class EntityManagerInitializer implements InitializingBean { diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/mapping/MvcEndpointMapping.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/mapping/MvcMappingRegister.java similarity index 65% rename from springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/mapping/MvcEndpointMapping.java rename to springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/mapping/MvcMappingRegister.java index cf2eff3b..dfe3ed39 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/mapping/MvcEndpointMapping.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/mapping/MvcMappingRegister.java @@ -10,10 +10,11 @@ import java.lang.reflect.Method; @AllArgsConstructor -public class MvcEndpointMapping { +public class MvcMappingRegister { private final RequestMappingHandlerMapping handlerMapping; + /** * add mvc mapping * @@ -37,4 +38,25 @@ public void addMapping(String url, RequestMethod requestMethod, Object handler, handlerMapping.registerMapping(mappingInfo, handler, method); } + /** + * remove mvc mapping + * + * @param url mapping url + * @param requestMethod request method + */ + public void removeMapping(String url, RequestMethod requestMethod) { + RequestMappingInfo.BuilderConfiguration options = new RequestMappingInfo.BuilderConfiguration(); + options.setPatternParser(new PathPatternParser()); + + RequestMappingInfo mappingInfo = RequestMappingInfo + .paths(url) + .methods(requestMethod) + .produces(MediaType.APPLICATION_JSON_VALUE) + .options(options) + .build(); + + handlerMapping.unregisterMapping(mappingInfo); + } + + } diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/registrar/DataFastBeanDefinitionRegistrar.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/registrar/DataFastBeanDefinitionRegistrar.java deleted file mode 100644 index 0a733ab7..00000000 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/registrar/DataFastBeanDefinitionRegistrar.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.codingapi.springboot.fast.registrar; - -import com.codingapi.springboot.fast.annotation.FastController; -import com.codingapi.springboot.framework.registrar.RegisterBeanScanner; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; -import org.springframework.core.type.AnnotationMetadata; - -import java.util.Set; - -@Slf4j -@Configuration -public class DataFastBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { - - - @SneakyThrows - @Override - public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { - RegisterBeanScanner registerBeanScanner = new RegisterBeanScanner(importingClassMetadata, FastController.class); - Set> classSet = registerBeanScanner.findTypes(); - - //register bean - for (Class clazz : classSet) { - log.info("scanner @FastController class:{}", clazz); - MvcMappingRegistrar.classSet.add(clazz); - } - } - - -} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/registrar/MvcMappingRegistrar.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/registrar/MvcMappingRegistrar.java deleted file mode 100644 index e6a154b8..00000000 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/registrar/MvcMappingRegistrar.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.codingapi.springboot.fast.registrar; - -import com.codingapi.springboot.fast.annotation.FastMapping; -import com.codingapi.springboot.fast.exception.FastMappingErrorException; -import com.codingapi.springboot.fast.executor.JpaExecutor; -import com.codingapi.springboot.fast.executor.MvcMethodInterceptor; -import com.codingapi.springboot.fast.mapping.MvcEndpointMapping; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.springframework.aop.Advisor; -import org.springframework.aop.framework.AdvisedSupport; -import org.springframework.aop.framework.AopProxy; -import org.springframework.aop.framework.AopProxyFactory; -import org.springframework.aop.framework.DefaultAopProxyFactory; -import org.springframework.data.domain.Pageable; -import org.springframework.util.StringUtils; - -import java.lang.reflect.Method; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -@Slf4j -public class MvcMappingRegistrar { - protected final static Set> classSet = new HashSet<>(); - private final MvcEndpointMapping mvcEndpointMapping; - - private final AopProxyFactory proxyFactory; - - private final List advisors; - - private final MvcMethodInterceptor interceptor; - - public MvcMappingRegistrar(MvcEndpointMapping mvcEndpointMapping, - JpaExecutor jpaExecutor, - List advisors) { - this.mvcEndpointMapping = mvcEndpointMapping; - this.advisors = advisors; - this.interceptor = new MvcMethodInterceptor(jpaExecutor); - this.proxyFactory = new DefaultAopProxyFactory(); - } - - @SneakyThrows - public void registerMvcMapping() { - for (Class clazz : classSet) { - Method[] methods = clazz.getDeclaredMethods(); - for (Method method : methods) { - FastMapping fastMapping = method.getAnnotation(FastMapping.class); - if (verify(fastMapping, method)) { - AdvisedSupport advisedSupport = createAdvisedSupport(clazz); - AopProxy proxy = proxyFactory.createAopProxy(advisedSupport); - mvcEndpointMapping.addMapping(fastMapping.mapping(), fastMapping.method(), - proxy.getProxy(), method); - } - } - } - } - - private AdvisedSupport createAdvisedSupport(Class clazz) { - AdvisedSupport advisedSupport = new AdvisedSupport(clazz); - advisedSupport.setTarget(interceptor); - advisedSupport.addAdvisors(advisors); - advisedSupport.addAdvice(interceptor); - return advisedSupport; - } - - private boolean verify(FastMapping fastMapping, Method method) throws FastMappingErrorException { - if (fastMapping == null) { - return false; - } - - if (!StringUtils.hasText(fastMapping.mapping())) { - throw new FastMappingErrorException(String.format("fast method %s missing mapping .", - method.getName())); - } - - if (!StringUtils.hasText(fastMapping.value())) { - throw new FastMappingErrorException(String.format("fast mapping %s missing value .", - fastMapping.mapping())); - } - - Class[] parameterTypes = method.getParameterTypes(); - for (Class parameter : parameterTypes) { - if (Pageable.class.isAssignableFrom(parameter)) { - if (!StringUtils.hasText(fastMapping.countQuery())) { - throw new FastMappingErrorException(String.format("fast mapping %s missing countQuery .", - fastMapping.mapping())); - } - } - } - return true; - } - -} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptMapping.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptMapping.java new file mode 100644 index 00000000..3fc32f3b --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptMapping.java @@ -0,0 +1,56 @@ +package com.codingapi.springboot.fast.script; + +import com.codingapi.springboot.framework.dto.response.MultiResponse; +import com.codingapi.springboot.framework.dto.response.Response; +import com.codingapi.springboot.framework.dto.response.SingleResponse; +import lombok.Getter; +import lombok.Setter; +import org.springframework.data.domain.Page; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.lang.reflect.Method; +import java.util.List; + + +@Setter +@Getter +public class ScriptMapping { + + private String mapping; + private ScriptMethod scriptMethod; + private String script; + + + public ScriptMapping(String mapping, ScriptMethod scriptMethod, String script) { + this.mapping = mapping; + this.scriptMethod = scriptMethod; + this.script = script; + } + + @ResponseBody + Response execute() { + Object result = ScriptRuntime.running(script); + if (result instanceof List || result.getClass().isArray()) { + return SingleResponse.of(result); + } else { + if (result instanceof MultiResponse) { + return (MultiResponse) result; + } + if (result instanceof Page) { + return MultiResponse.of((Page) result); + } + return SingleResponse.of(result); + } + } + + + Method getExecuteMethod() { + try { + return this.getClass().getDeclaredMethod("execute"); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptMappingRegister.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptMappingRegister.java new file mode 100644 index 00000000..a94e9134 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptMappingRegister.java @@ -0,0 +1,46 @@ +package com.codingapi.springboot.fast.script; + +import com.codingapi.springboot.fast.mapping.MvcMappingRegister; +import com.codingapi.springboot.framework.dto.response.Response; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class ScriptMappingRegister { + + private final MvcMappingRegister mappingRegister; + + /** + * test dynamic mapping + * + * @param scriptMapping dynamic mapping + **/ + public void addMapping(ScriptMapping scriptMapping) { + mappingRegister.addMapping(scriptMapping.getMapping(), scriptMapping.getScriptMethod().toRequestMethod(), + scriptMapping, scriptMapping.getExecuteMethod()); + } + + + /** + * test dynamic mapping + * + * @param scriptMapping dynamic mapping + * @return result + */ + public Response test(ScriptMapping scriptMapping) { + return scriptMapping.execute(); + } + + + /** + * remove mvc mapping + * + * @param url mapping url + * @param requestMethod request method + */ + public void removeMapping(String url, ScriptMethod scriptMethod){ + mappingRegister.removeMapping(url, scriptMethod.toRequestMethod()); + } + + + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptMethod.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptMethod.java new file mode 100644 index 00000000..8dfb44b6 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptMethod.java @@ -0,0 +1,18 @@ +package com.codingapi.springboot.fast.script; + +import org.springframework.web.bind.annotation.RequestMethod; + +public enum ScriptMethod { + + GET, POST; + + + public RequestMethod toRequestMethod() { + if (this == GET) { + return RequestMethod.GET; + } else { + return RequestMethod.POST; + } + } + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptRequest.java new file mode 100644 index 00000000..9b9f0e78 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptRequest.java @@ -0,0 +1,46 @@ +package com.codingapi.springboot.fast.script; + +import javax.servlet.http.HttpServletRequest; +import com.codingapi.springboot.framework.dto.request.PageRequest; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class ScriptRequest { + + private final HttpServletRequest request; + + public String getParameter(String key, String defaultValue) { + String result = request.getParameter(key); + return result == null ? defaultValue : result; + } + + public int getParameter(String key, int defaultValue) { + String result = request.getParameter(key); + return result == null ? defaultValue : Integer.parseInt(result); + } + + public float getParameter(String key, float defaultValue) { + String result = request.getParameter(key); + return result == null ? defaultValue : Float.parseFloat(result); + } + + public double getParameter(String key, double defaultValue) { + String result = request.getParameter(key); + return result == null ? defaultValue : Double.parseDouble(result); + } + + public long getParameter(String key, long defaultValue) { + String result = request.getParameter(key); + return result == null ? defaultValue : Long.parseLong(result); + } + + public boolean getParameter(String key, boolean defaultValue) { + String result = request.getParameter(key); + return result == null ? defaultValue : Boolean.parseBoolean(result); + } + + public PageRequest pageRequest(int pageNumber, int pageSize) { + return PageRequest.of(pageNumber, pageSize); + } + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptRuntime.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptRuntime.java new file mode 100644 index 00000000..2b5a63ba --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptRuntime.java @@ -0,0 +1,31 @@ +package com.codingapi.springboot.fast.script; + +import com.codingapi.springboot.fast.jdbc.JdbcQuery; +import com.codingapi.springboot.fast.jdbc.JdbcQueryContext; +import com.codingapi.springboot.fast.jpa.JPAQuery; +import com.codingapi.springboot.fast.jpa.JpaQueryContext; +import groovy.lang.Binding; +import groovy.lang.GroovyShell; +import groovy.lang.Script; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + + +public class ScriptRuntime { + + static Object running(String script) { + Binding binding = new Binding(); + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); + ScriptRequest request = new ScriptRequest(attributes.getRequest()); + JdbcQuery jdbcQuery = JdbcQueryContext.getInstance().getJdbcQuery(); + JPAQuery jpaQuery = JpaQueryContext.getInstance().getJPAQuery(); + + binding.setVariable("$request", request); + binding.setVariable("$jpa", jpaQuery); + binding.setVariable("$jdbc", jdbcQuery); + + GroovyShell groovyShell = new GroovyShell(binding); + Script userScript = groovyShell.parse(script); + return userScript.run(); + } +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/ISort.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/ISort.java deleted file mode 100644 index b4a689d3..00000000 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/ISort.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.codingapi.springboot.fast.sort; - -public interface ISort { - - Integer getSort(); - - void setSort(Integer sort); -} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/SortRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/SortRepository.java deleted file mode 100644 index 499ecd34..00000000 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/sort/SortRepository.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.codingapi.springboot.fast.sort; - -import com.codingapi.springboot.framework.dto.request.PageRequest; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.repository.NoRepositoryBean; - -import java.util.ArrayList; -import java.util.List; - -@NoRepositoryBean -public interface SortRepository extends JpaRepository { - - - default void pageSort(PageRequest request, List ids) { - List list = new ArrayList<>(); - for (int i = 0; i < ids.size(); i++) { - ISort entity = getReferenceById(ids.get(i)); - entity.setSort(i + (request.getPageNumber() * request.getPageSize())); - list.add((T) entity); - } - saveAll(list); - } - -} diff --git a/springboot-starter-data-fast/src/main/resources/META-INF/spring.factories b/springboot-starter-data-fast/src/main/resources/META-INF/spring.factories index 99726f46..f2d11256 100644 --- a/springboot-starter-data-fast/src/main/resources/META-INF/spring.factories +++ b/springboot-starter-data-fast/src/main/resources/META-INF/spring.factories @@ -1,4 +1,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.codingapi.springboot.fast.DataFastConfiguration,\ -com.codingapi.springboot.fast.registrar.DataFastBeanDefinitionRegistrar,\ -com.codingapi.springboot.fast.dynamic.DynamicConfiguration +com.codingapi.springboot.fast.jpa.JPAQueryConfiguration,\ +com.codingapi.springboot.fast.jdbc.JdbcQueryConfiguration \ No newline at end of file diff --git a/springboot-starter-data-fast/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/springboot-starter-data-fast/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index e8f74008..68b400af 100644 --- a/springboot-starter-data-fast/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/springboot-starter-data-fast/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,3 +1,3 @@ com.codingapi.springboot.fast.DataFastConfiguration -com.codingapi.springboot.fast.registrar.DataFastBeanDefinitionRegistrar -com.codingapi.springboot.fast.dynamic.DynamicConfiguration \ No newline at end of file +com.codingapi.springboot.fast.jpa.JPAQueryConfiguration +com.codingapi.springboot.fast.jdbc.JdbcQueryConfiguration \ No newline at end of file diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DataFastApplicationTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DataFastApplicationTest.java deleted file mode 100644 index 1870f2c4..00000000 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DataFastApplicationTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.codingapi.springboot.fast; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@AutoConfigureMockMvc -@SpringBootTest -public class DataFastApplicationTest { - - @Autowired - private MockMvc mockMvc; - - @Test - void findAll() throws Exception { - mockMvc.perform(get("/demo/findAll").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()); - } - - @Test - void findById() throws Exception { - mockMvc.perform(get("/demo/getById").param("id", "1").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()); - } - - @Test - void findPage() throws Exception { - mockMvc.perform(get("/demo/findPage").param("pageSize", "20").param("current", "1").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()); - } -} diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java index bb00452f..515c5550 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java @@ -5,19 +5,19 @@ import com.codingapi.springboot.framework.dto.request.Filter; import com.codingapi.springboot.framework.dto.request.PageRequest; import com.codingapi.springboot.framework.dto.request.Relation; +import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.Sort; -import org.springframework.transaction.annotation.Transactional; -import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +@Slf4j @SpringBootTest public class DemoRepositoryTest { @@ -70,14 +70,15 @@ void pageRequest() { PageRequest request = new PageRequest(); request.setCurrent(1); request.setPageSize(10); - request.addFilter("name", Relation.LIKE, "%2%"); + request.andFilter("name", Relation.LIKE, "%2%"); Page page = demoRepository.pageRequest(request); assertEquals(1, page.getTotalElements()); } + @Test - void customSearchOrFilters() { + void customInSearch() { demoRepository.deleteAll(); Demo demo1 = new Demo(); demo1.setName("123"); @@ -90,14 +91,17 @@ void customSearchOrFilters() { PageRequest request = new PageRequest(); request.setCurrent(1); request.setPageSize(10); - request.orFilters(Filter.as("name","123"),Filter.as("name","456")); + + request.andFilter("id", Relation.IN, 1, 2, 3); Page page = demoRepository.pageRequest(request); - assertEquals(2, page.getTotalElements()); + log.info("demo:{}", page.getContent()); +// assertEquals(2, page.getTotalElements()); } + @Test - void customSearchAddFilters() { + void customOrSearch() { demoRepository.deleteAll(); Demo demo1 = new Demo(); demo1.setName("123"); @@ -111,13 +115,14 @@ void customSearchAddFilters() { request.setCurrent(1); request.setPageSize(10); -// request.addFilters(Filter.as("id", Relation.IN, 1),Filter.as("id", Relation.IN, 2),Filter.as("id", Relation.IN, 3),Filter.as("id", Relation.IN, 4)); - request.andFilters(Filter.as("name", Relation.LIKE, "%2%"), Filter.and(Filter.as("id", Relation.IN, 1), Filter.as("id", Relation.IN, 2))); + + request.orFilters(Filter.as("id", Relation.IN, 1, 2, 3), Filter.as("name", "123")); + Page page = demoRepository.pageRequest(request); - assertEquals(0, page.getTotalElements()); + log.info("demo:{}", page.getContent()); +// assertEquals(2, page.getTotalElements()); } - @Test void dynamicListQuery() { demoRepository.deleteAll(); @@ -171,27 +176,4 @@ void sortQuery() { assertEquals(2, page.getTotalElements()); } - - @Test - @Transactional - void pageSort() { - demoRepository.deleteAll(); - Demo demo1 = new Demo(); - demo1.setName("123"); - demoRepository.save(demo1); - - Demo demo2 = new Demo(); - demo2.setName("456"); - demoRepository.save(demo2); - - List ids = Arrays.asList(demo1.getId(), demo2.getId()); - System.out.println(ids); - demoRepository.pageSort(PageRequest.of(1, 10), ids); - - Demo newDemo1 = demoRepository.getReferenceById(demo1.getId()); - Demo newDemo2 = demoRepository.getReferenceById(demo2.getId()); - - assertEquals(newDemo2.getSort(), 1); - assertEquals(newDemo1.getSort(), 0); - } } diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Demo.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Demo.java index 4de36433..b4370905 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Demo.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Demo.java @@ -1,18 +1,16 @@ package com.codingapi.springboot.fast.entity; -import com.codingapi.springboot.fast.sort.ISort; +import javax.persistence.*; import lombok.Getter; import lombok.Setter; - -import javax.persistence.*; - +import lombok.ToString; @Setter @Getter @Entity @Table(name = "t_demo") -public class Demo implements ISort { - +@ToString +public class Demo { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/query/FastDemoQuery.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/query/FastDemoQuery.java deleted file mode 100644 index 50dd5939..00000000 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/query/FastDemoQuery.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.codingapi.springboot.fast.query; - -import com.codingapi.springboot.fast.annotation.FastController; -import com.codingapi.springboot.fast.annotation.FastMapping; -import com.codingapi.springboot.fast.entity.Demo; -import com.codingapi.springboot.framework.dto.request.PageRequest; -import com.codingapi.springboot.framework.dto.response.MultiResponse; -import com.codingapi.springboot.framework.dto.response.SingleResponse; -import org.springframework.web.bind.annotation.RequestParam; - -@FastController -public interface FastDemoQuery { - - @FastMapping(value = "select d from Demo d", mapping = "/demo/findAll") - MultiResponse findAll(); - - @FastMapping(value = "select d from Demo d where d.id = ?1", mapping = "/demo/getById") - SingleResponse getById(@RequestParam("id") int id); - - @FastMapping(value = "select d from Demo d", countQuery = "select count(d) from Demo d", mapping = "/demo/findPage") - MultiResponse findPage(PageRequest pageRequest); - -} diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/DemoRepository.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/DemoRepository.java index 9f5c76af..30a688b1 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/DemoRepository.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/DemoRepository.java @@ -1,9 +1,8 @@ package com.codingapi.springboot.fast.repository; import com.codingapi.springboot.fast.entity.Demo; -import com.codingapi.springboot.fast.query.FastRepository; -import com.codingapi.springboot.fast.sort.SortRepository; +import com.codingapi.springboot.fast.jpa.repository.FastRepository; -public interface DemoRepository extends FastRepository, SortRepository { +public interface DemoRepository extends FastRepository { } diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/script/ScriptRuntimeTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/script/ScriptRuntimeTest.java new file mode 100644 index 00000000..19e758db --- /dev/null +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/script/ScriptRuntimeTest.java @@ -0,0 +1,16 @@ +package com.codingapi.springboot.fast.script; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@SpringBootTest +class ScriptRuntimeTest { + + @Test + void running() { + Object res = ScriptRuntime.running("return 1"); + assertEquals(1, res); + } +} \ No newline at end of file diff --git a/springboot-starter-id-generator/pom.xml b/springboot-starter-id-generator/pom.xml deleted file mode 100644 index 2df5ea76..00000000 --- a/springboot-starter-id-generator/pom.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - springboot-parent - com.codingapi.springboot - 2.7.5 - - 4.0.0 - - springboot-starter-id-generator - - - 8 - - - - - - org.xerial - sqlite-jdbc - - - - commons-dbutils - commons-dbutils - - - - org.springframework - spring-jdbc - - - - - \ No newline at end of file diff --git a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/AutoConfiguration.java b/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/AutoConfiguration.java deleted file mode 100644 index e9a8b394..00000000 --- a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/AutoConfiguration.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.codingapi.springboot.generator; - -import com.codingapi.springboot.generator.dao.IdKeyDao; -import com.codingapi.springboot.generator.properties.GeneratorJdbcProperties; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class AutoConfiguration { - - @Bean - @ConfigurationProperties(prefix = "codingapi.id.jdbc.generator") - public GeneratorJdbcProperties generatorJdbcProperties() { - return new GeneratorJdbcProperties(); - } - - @Bean(initMethod = "init") - @ConditionalOnMissingBean - public IdKeyDao idKeyDao(GeneratorJdbcProperties generatorJdbcProperties) { - IdKeyDao keyDao = new IdKeyDao(generatorJdbcProperties.openDataSource()); - IdGeneratorContext.getInstance().init(keyDao); - return keyDao; - } - -} diff --git a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/IdGenerate.java b/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/IdGenerate.java deleted file mode 100644 index 972459a8..00000000 --- a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/IdGenerate.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.codingapi.springboot.generator; - -public interface IdGenerate { - - default long generateLongId() { - return IdGeneratorContext.getInstance().generateId(getClass()); - } - - default int generateIntId() { - return (int) IdGeneratorContext.getInstance().generateId(getClass()); - } - - default String generateStringId() { - return String.valueOf(IdGeneratorContext.getInstance().generateId(getClass())); - } - -} diff --git a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/IdGeneratorContext.java b/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/IdGeneratorContext.java deleted file mode 100644 index c405ef30..00000000 --- a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/IdGeneratorContext.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.codingapi.springboot.generator; - -import com.codingapi.springboot.generator.dao.IdKeyDao; -import com.codingapi.springboot.generator.service.IdGenerateService; - -public class IdGeneratorContext { - - private static IdGeneratorContext instance; - private IdGenerateService idGenerateService; - - private IdGeneratorContext() { - } - - public static IdGeneratorContext getInstance() { - if (instance == null) { - synchronized (IdGeneratorContext.class) { - if (instance == null) { - instance = new IdGeneratorContext(); - } - } - } - return instance; - } - - long generateId(Class clazz) { - return idGenerateService.generateId(clazz); - } - - void init(IdKeyDao idKeyDao) { - idGenerateService = new IdGenerateService(idKeyDao); - } - -} diff --git a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/dao/DbHelper.java b/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/dao/DbHelper.java deleted file mode 100644 index 92b8191a..00000000 --- a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/dao/DbHelper.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.codingapi.springboot.generator.dao; - -import org.apache.commons.dbutils.QueryRunner; - -import javax.sql.DataSource; -import java.sql.Connection; -import java.sql.SQLException; - -public class DbHelper { - - private final DataSource dataSource; - private final QueryRunner queryRunner; - - - public DbHelper(DataSource dataSource) { - this.dataSource = dataSource; - this.queryRunner = new QueryRunner(); - } - - public void execute(IExecute execute) throws SQLException { - Connection connection = dataSource.getConnection(); - execute.execute(connection, queryRunner); - connection.close(); - } - - public T updateAndQuery(IUpdateAndQuery execute) throws SQLException { - Connection connection = dataSource.getConnection(); - connection.setAutoCommit(false); - T res = execute.updateAndQuery(connection, queryRunner); - connection.commit(); - connection.close(); - return res; - } - - public int update(IUpdate execute) throws SQLException { - Connection connection = dataSource.getConnection(); - connection.setAutoCommit(false); - int res = execute.update(connection, queryRunner); - connection.commit(); - connection.close(); - return res; - } - - public T query(IQuery execute) throws SQLException { - Connection connection = dataSource.getConnection(); - T res = execute.query(connection, queryRunner); - connection.close(); - return res; - } - - - interface IExecute { - default void execute(Connection connection, QueryRunner queryRunner) throws SQLException { - } - } - - - interface IUpdateAndQuery { - default T updateAndQuery(Connection connection, QueryRunner queryRunner) throws SQLException { - return null; - } - } - - interface IUpdate { - default int update(Connection connection, QueryRunner queryRunner) throws SQLException { - return 0; - } - } - - interface IQuery { - default T query(Connection connection, QueryRunner queryRunner) throws SQLException { - return null; - } - } - -} diff --git a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/dao/IdKeyDao.java b/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/dao/IdKeyDao.java deleted file mode 100644 index 541a68a5..00000000 --- a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/dao/IdKeyDao.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.codingapi.springboot.generator.dao; - -import com.codingapi.springboot.generator.domain.IdKey; -import lombok.SneakyThrows; -import org.apache.commons.dbutils.QueryRunner; -import org.apache.commons.dbutils.ResultSetHandler; - -import javax.sql.DataSource; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; - -public class IdKeyDao { - - private final DbHelper> dbHelper; - - private final ResultSetHandler> handler; - - public IdKeyDao(DataSource dataSource) { - this.dbHelper = new DbHelper<>(dataSource); - this.handler = rs -> { - List list = new ArrayList<>(); - while (rs.next()) { - IdKey generator = new IdKey(); - generator.setKey(rs.getString("TAG")); - generator.setId(rs.getInt("ID")); - generator.setUpdateTime(rs.getLong("UPDATE_TIME")); - list.add(generator); - } - return list; - }; - } - - @SneakyThrows - public void save(IdKey generator) { - dbHelper.update(new DbHelper.IUpdate() { - @Override - public int update(Connection connection, QueryRunner queryRunner) throws SQLException { - List list = queryRunner.query(connection, "SELECT * FROM ID_GENERATOR WHERE TAG = ?", handler, generator.getKey()); - if (list != null && list.size() > 0) { - return 0; - } - - String sql = "INSERT INTO ID_GENERATOR (ID, UPDATE_TIME, TAG) VALUES (?, ?, ?)"; - return queryRunner.update(connection, sql, generator.getId(), generator.getUpdateTime(), generator.getKey()); - } - }); - } - - - @SneakyThrows - public IdKey getByKey(String key) { - return dbHelper.query(new DbHelper.IQuery>() { - @Override - public List query(Connection connection, QueryRunner queryRunner) throws SQLException { - return queryRunner.query(connection, "SELECT * FROM ID_GENERATOR WHERE TAG = ?", handler, key); - } - }).stream().findFirst().orElse(null); - } - - - @SneakyThrows - public IdKey updateMaxId(IdKey generator) { - return dbHelper.updateAndQuery(new DbHelper.IUpdateAndQuery>() { - @Override - public List updateAndQuery(Connection connection, QueryRunner queryRunner) throws SQLException { - queryRunner.update(connection, "UPDATE ID_GENERATOR SET ID = ID + 1 WHERE TAG = ?", generator.getKey()); - return queryRunner.query(connection, "SELECT * FROM ID_GENERATOR WHERE TAG = ?", handler, generator.getKey()); - } - }).stream().findFirst().orElse(null); - } - - - @SneakyThrows - public List findAll() throws SQLException { - return dbHelper.query(new DbHelper.IQuery>() { - @Override - public List query(Connection connection, QueryRunner queryRunner) throws SQLException { - return queryRunner.query(connection, "SELECT * FROM ID_GENERATOR", handler); - } - }); - } - - private void init() throws SQLException { - dbHelper.execute(new DbHelper.IExecute() { - @Override - public void execute(Connection connection, QueryRunner queryRunner) throws SQLException { - String sql = "CREATE TABLE IF NOT EXISTS ID_GENERATOR (TAG TEXT NOT NULL, ID INTEGER NOT NULL, UPDATE_TIME INTEGER NOT NULL, PRIMARY KEY (TAG))"; - queryRunner.update(connection, sql); - } - }); - } -} diff --git a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/domain/IdKey.java b/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/domain/IdKey.java deleted file mode 100644 index 0363383e..00000000 --- a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/domain/IdKey.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.codingapi.springboot.generator.domain; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Setter -@Getter -@NoArgsConstructor -public class IdKey { - - private String key; - private long id; - private long updateTime; - - public IdKey(String key, long id) { - this.key = key; - this.id = id; - this.updateTime = System.currentTimeMillis(); - } - - public IdKey(String key) { - this.key = key; - this.id = 1; - this.updateTime = System.currentTimeMillis(); - } - - public IdKey(String key, long id, long updateTime) { - this.key = key; - this.id = id; - this.updateTime = updateTime; - } -} diff --git a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/properties/GeneratorJdbcProperties.java b/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/properties/GeneratorJdbcProperties.java deleted file mode 100644 index 6d89224a..00000000 --- a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/properties/GeneratorJdbcProperties.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.codingapi.springboot.generator.properties; - -import lombok.Getter; -import lombok.Setter; -import org.springframework.jdbc.datasource.DriverManagerDataSource; - -import javax.sql.DataSource; - -@Setter -@Getter -public class GeneratorJdbcProperties { - - private String jdbcUrl = "jdbc:sqlite:db.db"; - - private String jdbcDriver = "org.sqlite.JDBC"; - - private String jdbcUsername = "sa"; - - private String jdbcPassword = "sa"; - - public DataSource openDataSource() { - final DriverManagerDataSource dataSource = new DriverManagerDataSource(); - dataSource.setDriverClassName(jdbcDriver); - dataSource.setUrl(jdbcUrl); - dataSource.setUsername(jdbcUsername); - dataSource.setPassword(jdbcPassword); - return dataSource; - } -} diff --git a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/service/IdGenerateService.java b/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/service/IdGenerateService.java deleted file mode 100644 index 9d7dc478..00000000 --- a/springboot-starter-id-generator/src/main/java/com/codingapi/springboot/generator/service/IdGenerateService.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.codingapi.springboot.generator.service; - -import com.codingapi.springboot.generator.dao.IdKeyDao; -import com.codingapi.springboot.generator.domain.IdKey; - -public class IdGenerateService { - - private final IdKeyDao keyDao; - - public IdGenerateService(IdKeyDao keyDao) { - this.keyDao = keyDao; - } - - public synchronized long generateId(Class clazz) { - IdKey idKey = keyDao.getByKey(clazz.getName()); - if (idKey == null) { - idKey = new IdKey(clazz.getName()); - keyDao.save(idKey); - } else { - keyDao.updateMaxId(idKey); - } - return idKey.getId(); - } -} diff --git a/springboot-starter-id-generator/src/main/resources/META-INF/spring.factories b/springboot-starter-id-generator/src/main/resources/META-INF/spring.factories deleted file mode 100644 index f30445ef..00000000 --- a/springboot-starter-id-generator/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,2 +0,0 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -com.codingapi.springboot.generator.AutoConfiguration \ No newline at end of file diff --git a/springboot-starter-id-generator/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/springboot-starter-id-generator/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index c1aa4e07..00000000 --- a/springboot-starter-id-generator/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1 +0,0 @@ -com.codingapi.springboot.generator.AutoConfiguration \ No newline at end of file diff --git a/springboot-starter-id-generator/src/test/java/com/codingapi/springboot/generator/IdGenerateServiceTest.java b/springboot-starter-id-generator/src/test/java/com/codingapi/springboot/generator/IdGenerateServiceTest.java deleted file mode 100644 index a2176a7c..00000000 --- a/springboot-starter-id-generator/src/test/java/com/codingapi/springboot/generator/IdGenerateServiceTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.codingapi.springboot.generator; - -import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -@Slf4j -@SpringBootTest -class IdGenerateServiceTest { - - @Test - void generateId() { - for (int i = 0; i < 1000; i++) { - long id = IdGeneratorContext.getInstance().generateId(IdGenerateServiceTest.class); - log.info("id=>{}", id); - assertTrue(id > 0, "id generator error."); - } - } -} \ No newline at end of file diff --git a/springboot-starter-id-generator/src/test/java/com/codingapi/springboot/generator/IdGeneratorApplication.java b/springboot-starter-id-generator/src/test/java/com/codingapi/springboot/generator/IdGeneratorApplication.java deleted file mode 100644 index d7979615..00000000 --- a/springboot-starter-id-generator/src/test/java/com/codingapi/springboot/generator/IdGeneratorApplication.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.codingapi.springboot.generator; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class IdGeneratorApplication { - - public static void main(String[] args) { - SpringApplication.run(IdGeneratorApplication.class, args); - } -} diff --git a/springboot-starter-id-generator/src/test/resources/application.properties b/springboot-starter-id-generator/src/test/resources/application.properties deleted file mode 100644 index 8a3b6d8a..00000000 --- a/springboot-starter-id-generator/src/test/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -codingapi.id.generator.jdbc-url=jdbc:h2:file:./test.db \ No newline at end of file diff --git a/springboot-starter-security-jwt/pom.xml b/springboot-starter-security-jwt/pom.xml index 97b1705d..fb8de0b4 100644 --- a/springboot-starter-security-jwt/pom.xml +++ b/springboot-starter-security-jwt/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.7.5 + 2.7.6 springboot-starter-security-jwt diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 85bb6c14..a6283042 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.7.5 + 2.7.6 springboot-starter @@ -21,6 +21,16 @@ spring-data-commons + + jakarta.persistence + jakarta.persistence-api + + + + com.fasterxml.jackson.core + jackson-databind + + org.springframework spring-tx diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/IdRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/IdRequest.java new file mode 100644 index 00000000..b325423e --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/IdRequest.java @@ -0,0 +1,33 @@ +package com.codingapi.springboot.framework.dto.request; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class IdRequest { + + private String id; + + public String getStringId(){ + return id; + } + + public int getIntId(){ + return Integer.parseInt(id); + } + + public Long getLongId(){ + return Long.parseLong(id); + } + + public float getFloatId(){ + return Float.parseFloat(id); + } + + public double getDoubleId(){ + return Double.parseDouble(id); + } + + +} diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java index 7c0eb2df..19a9282a 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java @@ -24,7 +24,7 @@ public class PageRequest extends org.springframework.data.domain.PageRequest { private org.springframework.data.domain.PageRequest pageRequest; public PageRequest(int current, int pageSize, Sort sort) { - super(current > 0 ? current-- : 0, pageSize, sort); + super(current, pageSize, sort); this.current = current; this.pageSize = pageSize; this.pageRequest = org.springframework.data.domain.PageRequest.of(current, pageSize, sort); @@ -99,17 +99,17 @@ public Sort getSort() { } @Override - public org.springframework.data.domain.PageRequest next() { + public PageRequest next() { return new PageRequest(current + 1, getPageSize(), getSort()); } @Override - public org.springframework.data.domain.PageRequest previous() { + public PageRequest previous() { return current == 0 ? this : new PageRequest(current - 1, getPageSize(), getSort()); } @Override - public org.springframework.data.domain.PageRequest first() { + public PageRequest first() { return new PageRequest(0, getPageSize(), getSort()); } @@ -162,7 +162,7 @@ public void addSort(Sort sort) { } } - public PageRequest addFilter(String key, Relation relation, Object... value) { + public PageRequest andFilter(String key, Relation relation, Object... value) { requestFilter.addFilter(key, relation, value); return this; } @@ -172,8 +172,8 @@ public PageRequest addFilter(String key, Object... value) { return this; } - public PageRequest andFilters(Filter... filters) { - requestFilter.andFilters(filters); + public PageRequest andFilter(Filter... value) { + requestFilter.andFilters(value); return this; } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/em/IEnum.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/em/IEnum.java new file mode 100644 index 00000000..d24151b1 --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/em/IEnum.java @@ -0,0 +1,6 @@ +package com.codingapi.springboot.framework.em; + +public interface IEnum { + + int getCode(); +} diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/serializable/EnumSerializer.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/serializable/EnumSerializer.java new file mode 100644 index 00000000..338dcf20 --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/serializable/EnumSerializer.java @@ -0,0 +1,16 @@ +package com.codingapi.springboot.framework.serializable; + +import com.codingapi.springboot.framework.em.IEnum; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +public class EnumSerializer extends JsonSerializer { + + @Override + public void serialize(IEnum iEnum, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeNumber(iEnum.getCode()); + } +} From 375e15aac88deedd6d15d0f82675cded0487da22 Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Tue, 2 Jan 2024 14:45:29 +0800 Subject: [PATCH 021/101] update 2.7.6 --- README.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/README.md b/README.md index a3ddf406..16844a72 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@ v.3.x 为springboot 3.x版本,使用jdk17版本 * springboot-starter | Springboot领域驱动框架 * springboot-starter-data-fast | 快速数据呈现框架 -* springboot-starter-id-generator | Id自增策略框架 * springboot-starter-security-jwt | security&jwt权限框架 ## SpringBoot DDD Architecture | SpringBoot DDD 框架图 @@ -42,13 +41,6 @@ v.3.x 为springboot 3.x版本,使用jdk17版本 ${last.version} - - - com.codingapi.springboot - springboot-starter-id-generator - ${last.version} - - com.codingapi.springboot From abb8b4c8de4f2ae9bab03df53d0154e598f5389a Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Tue, 2 Jan 2024 22:16:16 +0800 Subject: [PATCH 022/101] add 2.7.7.dev --- pom.xml | 22 ++++++++++++++++++---- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-security-jwt/pom.xml | 2 +- springboot-starter/pom.xml | 4 ++-- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index f41008e8..59fc5002 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.7.6 + 2.7.7.dev https://github.com/codingapi/springboot-framewrok springboot-parent @@ -38,10 +38,11 @@ 1.11.0 0.10.2 0.9.16 - 1.70 + 1.77 1.2.0 2.2 4.0.15 + 1.3.12 @@ -74,10 +75,23 @@ + + ch.qos.logback + logback-classic + ${logback.version} + + + + ch.qos.logback + logback-core + ${logback.version} + + + org.bouncycastle - bcprov-jdk15on - ${bcprov-jdk15on.version} + bcprov-jdk18on + ${bcprov-jdk18on.version} diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index b6e0468e..553bbcd3 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.6 + 2.7.7.dev 4.0.0 diff --git a/springboot-starter-security-jwt/pom.xml b/springboot-starter-security-jwt/pom.xml index fb8de0b4..e349a1b9 100644 --- a/springboot-starter-security-jwt/pom.xml +++ b/springboot-starter-security-jwt/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.7.6 + 2.7.7.dev springboot-starter-security-jwt diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index a6283042..00ace6d5 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.7.6 + 2.7.7.dev springboot-starter @@ -48,7 +48,7 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on From f8b505b5cf8addf788a9200ccd3e1cd50c429f43 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Tue, 2 Jan 2024 22:30:37 +0800 Subject: [PATCH 023/101] fix h2database version --- pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pom.xml b/pom.xml index 59fc5002..679da4bd 100644 --- a/pom.xml +++ b/pom.xml @@ -43,6 +43,7 @@ 2.2 4.0.15 1.3.12 + 2.2.224 @@ -75,6 +76,12 @@ + + com.h2database + h2 + ${h2.version} + + ch.qos.logback logback-classic From 3b6f11f8e4df1f3fbc01401bf2074e50ccf29e01 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Tue, 2 Jan 2024 22:37:39 +0800 Subject: [PATCH 024/101] fix docs --- docs/wiki/home.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/docs/wiki/home.md b/docs/wiki/home.md index df4ad067..4387f03d 100644 --- a/docs/wiki/home.md +++ b/docs/wiki/home.md @@ -16,14 +16,7 @@ maven install com.codingapi.springboot springboot-starter-data-fast ${last.version} - - - - - com.codingapi.springboot - springboot-starter-id-generator - ${last.version} - + @@ -36,6 +29,5 @@ maven install [springboot-starter](./springboot-starter) [springboot-starter-security-jwt](./springboot-starter-security-jwt) -[springboot-starter-id-generator](./springboot-starter-id-generator) [springboot-starter-data-fast](./springboot-starter-data-fast.md) \ No newline at end of file From 27669516a3e4c82ae0aadc97391bc44c8492500b Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Tue, 2 Jan 2024 22:46:22 +0800 Subject: [PATCH 025/101] fix test --- pom.xml | 14 -------------- .../src/test/resources/application.properties | 1 + .../src/test/resources/application.properties | 1 + 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 679da4bd..fce76f60 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,6 @@ 1.2.0 2.2 4.0.15 - 1.3.12 2.2.224 @@ -82,19 +81,6 @@ ${h2.version} - - ch.qos.logback - logback-classic - ${logback.version} - - - - ch.qos.logback - logback-core - ${logback.version} - - - org.bouncycastle bcprov-jdk18on diff --git a/springboot-starter-data-fast/src/test/resources/application.properties b/springboot-starter-data-fast/src/test/resources/application.properties index c3ec648b..559cfc80 100644 --- a/springboot-starter-data-fast/src/test/resources/application.properties +++ b/springboot-starter-data-fast/src/test/resources/application.properties @@ -1,5 +1,6 @@ server.port=8088 spring.datasource.driver-class-name=org.h2.Driver spring.datasource.url=jdbc:h2:file:./test.db +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.show-sql=true \ No newline at end of file diff --git a/springboot-starter/src/test/resources/application.properties b/springboot-starter/src/test/resources/application.properties index c3ec648b..559cfc80 100644 --- a/springboot-starter/src/test/resources/application.properties +++ b/springboot-starter/src/test/resources/application.properties @@ -1,5 +1,6 @@ server.port=8088 spring.datasource.driver-class-name=org.h2.Driver spring.datasource.url=jdbc:h2:file:./test.db +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.show-sql=true \ No newline at end of file From 4e8c9d75ae351875cda69a1be9f64e128bea9096 Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Wed, 3 Jan 2024 11:07:20 +0800 Subject: [PATCH 026/101] support FastRepository#pageRequest x.x.x search --- .../fast/jpa/repository/QueryRequest.java | 106 ++++++++++++++---- .../springboot/fast/UserRepositoryTest.java | 92 +++++++++++++++ .../springboot/fast/entity/Demo.java | 5 +- .../springboot/fast/entity/Profile.java | 21 ++++ .../springboot/fast/entity/User.java | 22 ++++ .../fast/repository/ProfileRepository.java | 9 ++ .../fast/repository/UserRepository.java | 9 ++ 7 files changed, 239 insertions(+), 25 deletions(-) create mode 100644 springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/UserRepositoryTest.java create mode 100644 springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Profile.java create mode 100644 springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/User.java create mode 100644 springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/ProfileRepository.java create mode 100644 springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/UserRepository.java diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/QueryRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/QueryRequest.java index 4cf15162..5d179405 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/QueryRequest.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/QueryRequest.java @@ -3,17 +3,20 @@ import com.codingapi.springboot.framework.dto.request.Filter; import com.codingapi.springboot.framework.dto.request.PageRequest; import com.codingapi.springboot.framework.dto.request.RequestFilter; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.Order; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import org.springframework.beans.BeanUtils; import org.springframework.data.domain.Example; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; +import javax.persistence.criteria.*; import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Set; public class QueryRequest { @@ -52,10 +55,22 @@ public Example getExample() { private List getClazzProperties() { + return getClazzProperties(clazz); + } + + private List getClazzProperties(Class clazz) { List properties = new ArrayList<>(); PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(clazz); for (PropertyDescriptor descriptor : descriptors) { - properties.add(descriptor.getName()); + Class propertyType = descriptor.getPropertyType(); + String name = descriptor.getName(); + if (propertyType.getPackage() != null && !propertyType.getPackage().getName().startsWith("java")) { + List childProperties = getClazzProperties(propertyType); + for (String child : childProperties) { + properties.add(name + "." + child); + } + } + properties.add(name); } return properties; } @@ -73,91 +88,113 @@ public List getOrder(Root root, CriteriaBuilder criteriaBuilder) { return orderList; } + private Path getPathByKey(Root root, String key) { + String[] keys = key.split("\\."); + if (keys.length > 1) { + String lastKey = keys[keys.length-1]; + From current = root; + for (int i=0;i< keys.length-1;i++) { + String item = keys[i]; + Set joins = current.getJoins(); + for (Join join : joins) { + if (join.getModel().getBindableJavaType().getSimpleName().equalsIgnoreCase(item)) { + current = join; + } + } + } + return current.get(lastKey); + } else { + return root.get(key); + } + } + private Predicate toPredicate(Filter filter, CriteriaBuilder criteriaBuilder, Root root, List properties) { String key = filter.getKey(); if (filter.isAndFilters() || filter.isOrFilters() || properties.contains(key)) { + Path path = getPathByKey(root, key); + if (filter.isEqual()) { - return criteriaBuilder.equal(root.get(key), filter.getValue()[0]); + return criteriaBuilder.equal(path, filter.getValue()[0]); } if (filter.isLike()) { String matchValue = (String) filter.getValue()[0]; - return criteriaBuilder.like(root.get(key), "%" + matchValue + "%"); + return criteriaBuilder.like(path, "%" + matchValue + "%"); } if (filter.isBetween()) { Object value1 = filter.getValue()[0]; Object value2 = filter.getValue()[2]; if (value1 instanceof Integer && value2 instanceof Integer) { - return criteriaBuilder.between(root.get(key), (Integer) value1, (Integer) value2); + return criteriaBuilder.between(path, (Integer) value1, (Integer) value2); } if (value1 instanceof Long && value2 instanceof Long) { - return criteriaBuilder.between(root.get(key), (Long) value1, (Long) value2); + return criteriaBuilder.between(path, (Long) value1, (Long) value2); } if (value1 instanceof Date && value2 instanceof Date) { - return criteriaBuilder.between(root.get(key), (Date) value1, (Date) value2); + return criteriaBuilder.between(path, (Date) value1, (Date) value2); } } if (filter.isGreaterThan()) { Object value = filter.getValue()[0]; if (value instanceof Integer) { - return criteriaBuilder.greaterThan(root.get(key), (Integer) value); + return criteriaBuilder.greaterThan(path, (Integer) value); } if (value instanceof Long) { - return criteriaBuilder.greaterThan(root.get(key), (Long) value); + return criteriaBuilder.greaterThan(path, (Long) value); } if (value instanceof Date) { - return criteriaBuilder.greaterThan(root.get(key), (Date) value); + return criteriaBuilder.greaterThan(path, (Date) value); } } if (filter.isGreaterThanEqual()) { Object value = filter.getValue()[0]; if (value instanceof Integer) { - return criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Integer) value); + return criteriaBuilder.greaterThanOrEqualTo(path, (Integer) value); } if (value instanceof Long) { - return criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Long) value); + return criteriaBuilder.greaterThanOrEqualTo(path, (Long) value); } if (value instanceof Date) { - return criteriaBuilder.greaterThanOrEqualTo(root.get(key), (Date) value); + return criteriaBuilder.greaterThanOrEqualTo(path, (Date) value); } } if (filter.isLessThan()) { Object value = filter.getValue()[0]; if (value instanceof Integer) { - return criteriaBuilder.lessThan(root.get(key), (Integer) value); + return criteriaBuilder.lessThan(path, (Integer) value); } if (value instanceof Long) { - return criteriaBuilder.lessThan(root.get(key), (Long) value); + return criteriaBuilder.lessThan(path, (Long) value); } if (value instanceof Date) { - return criteriaBuilder.lessThan(root.get(key), (Date) value); + return criteriaBuilder.lessThan(path, (Date) value); } } if (filter.isLessThanEqual()) { Object value = filter.getValue()[0]; if (value instanceof Integer) { - return criteriaBuilder.lessThanOrEqualTo(root.get(key), (Integer) value); + return criteriaBuilder.lessThanOrEqualTo(path, (Integer) value); } if (value instanceof Long) { - return criteriaBuilder.lessThanOrEqualTo(root.get(key), (Long) value); + return criteriaBuilder.lessThanOrEqualTo(path, (Long) value); } if (value instanceof Date) { - return criteriaBuilder.lessThanOrEqualTo(root.get(key), (Date) value); + return criteriaBuilder.lessThanOrEqualTo(path, (Date) value); } } if (filter.isIn()) { Object[] value = filter.getValue(); - CriteriaBuilder.In in = criteriaBuilder.in(root.get(key)); + CriteriaBuilder.In in = criteriaBuilder.in(path); for (Object item : value) { in.value(item); } @@ -192,10 +229,33 @@ private Predicate toPredicate(Filter filter, CriteriaBuilder criteriaBuilder } + private void fetchJoins(From root, Class clazz) { + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + if (field.getAnnotation(OneToOne.class) != null) { + Join join = root.join(field.getName(), JoinType.INNER); + this.fetchJoins(join, field.getType()); + } + if (field.getAnnotation(ManyToOne.class) != null) { + Join join = root.join(field.getName(), JoinType.INNER); + this.fetchJoins(join, field.getType()); + } + if (field.getAnnotation(OneToMany.class) != null) { + Join join = root.join(field.getName(), JoinType.INNER); + this.fetchJoins(join, field.getType()); + } + if (field.getAnnotation(ManyToMany.class) != null) { + Join join = root.join(field.getName(), JoinType.INNER); + this.fetchJoins(join, field.getType()); + } + } + } + public List getPredicate(Root root, CriteriaBuilder criteriaBuilder) { List predicates = new ArrayList<>(); List properties = getClazzProperties(); RequestFilter requestFilter = request.getRequestFilter(); + this.fetchJoins(root, clazz); for (Filter filter : requestFilter.getFilters()) { Predicate predicate = toPredicate(filter, criteriaBuilder, root, properties); if (predicate != null) { diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/UserRepositoryTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/UserRepositoryTest.java new file mode 100644 index 00000000..1047c25b --- /dev/null +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/UserRepositoryTest.java @@ -0,0 +1,92 @@ +package com.codingapi.springboot.fast; + +import com.codingapi.springboot.fast.entity.Demo; +import com.codingapi.springboot.fast.entity.Profile; +import com.codingapi.springboot.fast.entity.User; +import com.codingapi.springboot.fast.repository.DemoRepository; +import com.codingapi.springboot.fast.repository.ProfileRepository; +import com.codingapi.springboot.fast.repository.UserRepository; +import com.codingapi.springboot.framework.dto.request.PageRequest; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Page; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Slf4j +@SpringBootTest +public class UserRepositoryTest { + + @Autowired + private DemoRepository demoRepository; + + @Autowired + private UserRepository userRepository; + + @Autowired + private ProfileRepository profileRepository; + + + @Test + void test1() { + + Demo demo = new Demo(); + demo.setName("123"); + demoRepository.save(demo); + + + Profile profile = new Profile(); + profile.setDemo(demo); + profile.setName("123"); + profileRepository.save(profile); + + + User user = new User(); + user.setName("li"); + user.setProfile(profile); + userRepository.save(user); + + assertTrue(demo.getId()>0); + assertTrue(user.getId()>0); + + PageRequest request = new PageRequest(); + request.addFilter("profile.demo.id",1); + Page page = userRepository.pageRequest(request); + System.out.println(page.getContent()); + + } + + + + @Test + void test2() { + + Demo demo = new Demo(); + demo.setName("123"); + demoRepository.save(demo); + + + Profile profile = new Profile(); + profile.setDemo(demo); + profile.setName("123"); + profileRepository.save(profile); + + + User user = new User(); + user.setName("li"); + user.setProfile(profile); + userRepository.save(user); + + assertTrue(demo.getId()>0); + assertTrue(user.getId()>0); + + PageRequest request = new PageRequest(); + request.addFilter("name","li"); + Page page = userRepository.pageRequest(request); + System.out.println(page.getContent()); + + } + +} diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Demo.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Demo.java index b4370905..b0c922d7 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Demo.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Demo.java @@ -1,15 +1,16 @@ package com.codingapi.springboot.fast.entity; -import javax.persistence.*; import lombok.Getter; import lombok.Setter; import lombok.ToString; +import javax.persistence.*; + @Setter @Getter @Entity -@Table(name = "t_demo") @ToString +@Table(name = "t_demo") public class Demo { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Profile.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Profile.java new file mode 100644 index 00000000..3ef6bb4a --- /dev/null +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Profile.java @@ -0,0 +1,21 @@ +package com.codingapi.springboot.fast.entity; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; + +@Entity +@Setter +@Getter +public class Profile { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + private String name; + + @OneToOne + private Demo demo; +} diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/User.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/User.java new file mode 100644 index 00000000..ab0b8815 --- /dev/null +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/User.java @@ -0,0 +1,22 @@ +package com.codingapi.springboot.fast.entity; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; + +@Entity +@Setter +@Getter +@Table(name = "t_user") +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + private String name; + + @OneToOne + private Profile profile; +} diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/ProfileRepository.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/ProfileRepository.java new file mode 100644 index 00000000..51a377e8 --- /dev/null +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/ProfileRepository.java @@ -0,0 +1,9 @@ +package com.codingapi.springboot.fast.repository; + +import com.codingapi.springboot.fast.entity.Profile; +import com.codingapi.springboot.fast.jpa.repository.FastRepository; + +public interface ProfileRepository extends FastRepository { + + +} diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/UserRepository.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/UserRepository.java new file mode 100644 index 00000000..e92382b0 --- /dev/null +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/UserRepository.java @@ -0,0 +1,9 @@ +package com.codingapi.springboot.fast.repository; + +import com.codingapi.springboot.fast.entity.User; +import com.codingapi.springboot.fast.jpa.repository.FastRepository; + +public interface UserRepository extends FastRepository { + + +} From e44a9b19d1710a6d9f0ee5644f0501533b69bc1f Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Thu, 4 Jan 2024 23:54:35 +0800 Subject: [PATCH 027/101] fix FastRepository --- .../fast/jpa/repository/DynamicRequest.java | 138 +++++++++ .../fast/jpa/repository/ExampleRequest.java | 46 +++ .../fast/jpa/repository/FastRepository.java | 19 +- .../fast/jpa/repository/QueryRequest.java | 267 ------------------ .../springboot/fast/MenuRepositoryTest.java | 60 ++++ .../springboot/fast/entity/Menu.java | 22 ++ .../fast/repository/MenuRepository.java | 9 + .../framework/dto/request/PageRequest.java | 4 +- .../framework/dto/request/RequestFilter.java | 6 +- 9 files changed, 287 insertions(+), 284 deletions(-) create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRequest.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/ExampleRequest.java delete mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/QueryRequest.java create mode 100644 springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/MenuRepositoryTest.java create mode 100644 springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Menu.java create mode 100644 springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/MenuRepository.java diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRequest.java new file mode 100644 index 00000000..59035275 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRequest.java @@ -0,0 +1,138 @@ +package com.codingapi.springboot.fast.jpa.repository; + +import com.codingapi.springboot.framework.dto.request.Filter; +import com.codingapi.springboot.framework.dto.request.PageRequest; +import com.codingapi.springboot.framework.dto.request.RequestFilter; +import org.springframework.data.domain.Sort; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class DynamicRequest { + + private final PageRequest request; + private final Class clazz; + + private final List params = new ArrayList<>(); + private int paramIndex = 1; + + public DynamicRequest(PageRequest request, Class clazz) { + this.request = request; + this.clazz = clazz; + } + + + public String getHql() { + StringBuilder hql = new StringBuilder("FROM " + clazz.getSimpleName() + " WHERE "); + RequestFilter requestFilter = request.getRequestFilter(); + if (requestFilter.hasFilter()) { + List filters = requestFilter.getFilters(); + for (int i = 0; i < filters.size(); i++) { + Filter filter = filters.get(i); + this.buildSQL(filter, hql); + if (i != filters.size() - 1) { + hql.append(" AND "); + } + } + } + + Sort sort = request.getSort(); + if (sort.isSorted()) { + hql.append(" ORDER BY "); + List orders = sort.toList(); + for (int i = 0; i < orders.size(); i++) { + Sort.Order order = orders.get(i); + hql.append(order.getProperty()).append(" ").append(order.getDirection().name()); + if (i != orders.size() - 1) { + hql.append(","); + } + } + } + + System.out.println(hql.toString()); + System.out.println(params); + return hql.toString(); + } + + + private void buildSQL(Filter filter, StringBuilder hql) { + if (filter.isOrFilters()) { + Filter[] orFilters = (Filter[]) filter.getValue(); + if (orFilters.length > 0) { + hql.append(" ( "); + for (int i = 0; i < orFilters.length; i++) { + Filter orFilter = orFilters[i]; + this.buildSQL(orFilter, hql); + if (i != orFilters.length - 1) { + hql.append(" OR "); + } + + } + hql.append(" )"); + } + } + + if (filter.isAndFilters()) { + Filter[] andFilters = (Filter[]) filter.getValue(); + if (andFilters.length > 0) { + hql.append(" ( "); + for (int i = 0; i < andFilters.length; i++) { + Filter andFilter = andFilters[i]; + this.buildSQL(andFilter, hql); + if (i != andFilters.length - 1) { + hql.append(" AND "); + } + } + hql.append(" )"); + } + } + + if (filter.isEqual()) { + hql.append(filter.getKey()).append(" = ?").append(paramIndex); + params.add(filter.getValue()[0]); + paramIndex++; + } + if (filter.isLike()) { + hql.append(filter.getKey()).append(" LIKE ?").append(paramIndex); + params.add("%" + filter.getValue()[0] + "%"); + paramIndex++; + } + if (filter.isIn()) { + hql.append(filter.getKey()).append(" IN (").append("?").append(paramIndex).append(")"); + params.add(Arrays.asList(filter.getValue())); + paramIndex++; + } + if (filter.isGreaterThan()) { + hql.append(filter.getKey()).append(" > ?").append(paramIndex); + params.add(filter.getValue()[0]); + paramIndex++; + } + if (filter.isLessThan()) { + hql.append(filter.getKey()).append(" < ?").append(paramIndex); + params.add(filter.getValue()[0]); + paramIndex++; + } + if (filter.isGreaterThanEqual()) { + hql.append(filter.getKey()).append(" >= ?").append(paramIndex); + params.add(filter.getValue()[0]); + paramIndex++; + } + if (filter.isLessThanEqual()) { + hql.append(filter.getKey()).append(" <= ?").append(paramIndex); + params.add(filter.getValue()[0]); + paramIndex++; + } + if (filter.isBetween()) { + hql.append(filter.getKey()).append(" BETWEEN ?").append(paramIndex).append(" AND ?").append(paramIndex + 1); + params.add(filter.getValue()[0]); + params.add(filter.getValue()[1]); + paramIndex += 2; + } + } + + + public Object[] getParams() { + return params.toArray(); + } +} \ No newline at end of file diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/ExampleRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/ExampleRequest.java new file mode 100644 index 00000000..25ee2ed2 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/ExampleRequest.java @@ -0,0 +1,46 @@ +package com.codingapi.springboot.fast.jpa.repository; + +import com.codingapi.springboot.framework.dto.request.Filter; +import com.codingapi.springboot.framework.dto.request.PageRequest; +import com.codingapi.springboot.framework.dto.request.RequestFilter; +import org.springframework.beans.BeanUtils; +import org.springframework.data.domain.Example; + +import java.beans.PropertyDescriptor; + +public class ExampleRequest { + + private final PageRequest request; + private final Class clazz; + + public ExampleRequest(PageRequest request, Class clazz) { + this.request = request; + this.clazz = clazz; + } + + public Example getExample() { + RequestFilter requestFilter = request.getRequestFilter(); + if (!requestFilter.hasFilter()) { + return null; + } + Object entity = null; + try { + entity = clazz.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(clazz); + for (PropertyDescriptor descriptor : descriptors) { + String name = descriptor.getName(); + Filter value = requestFilter.getFilter(name); + if (value != null) { + try { + descriptor.getWriteMethod().invoke(entity, value.getFilterValue(descriptor.getPropertyType())); + } catch (Exception e) { + } + } + } + return (Example) Example.of(entity); + } + +} \ No newline at end of file diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java index 3e2f86bb..4b55b787 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java @@ -1,25 +1,20 @@ package com.codingapi.springboot.fast.jpa.repository; import com.codingapi.springboot.framework.dto.request.PageRequest; -import javax.persistence.criteria.Order; -import javax.persistence.criteria.Predicate; import org.springframework.core.ResolvableType; import org.springframework.data.domain.Page; -import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.NoRepositoryBean; -import java.util.List; - @NoRepositoryBean public interface FastRepository extends JpaRepository, JpaSpecificationExecutor, DynamicRepository { default Page findAll(PageRequest request) { if (request.hasFilter()) { Class clazz = getDomainClass(); - QueryRequest queryRequest = new QueryRequest(request, clazz); - return findAll(queryRequest.getExample(), request); + ExampleRequest exampleRequest = new ExampleRequest(request, clazz); + return findAll(exampleRequest.getExample(), request); } return findAll((org.springframework.data.domain.PageRequest) request); } @@ -35,14 +30,8 @@ default Class getDomainClass() { default Page pageRequest(PageRequest request) { if (request.hasFilter()) { Class clazz = getDomainClass(); - Specification specification = (root, query, criteriaBuilder) -> { - QueryRequest queryRequest = new QueryRequest(request, clazz); - List predicates = queryRequest.getPredicate(root, criteriaBuilder); - List orderList = queryRequest.getOrder(root, criteriaBuilder); - return query.where(predicates.toArray(new Predicate[0])).orderBy(orderList).getRestriction(); - }; - - return findAll(specification, request); + DynamicRequest dynamicRequest = new DynamicRequest(request,clazz); + return dynamicPageQuery(dynamicRequest.getHql(), request, dynamicRequest.getParams()); } return findAll((org.springframework.data.domain.PageRequest) request); } diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/QueryRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/QueryRequest.java deleted file mode 100644 index 5d179405..00000000 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/QueryRequest.java +++ /dev/null @@ -1,267 +0,0 @@ -package com.codingapi.springboot.fast.jpa.repository; - -import com.codingapi.springboot.framework.dto.request.Filter; -import com.codingapi.springboot.framework.dto.request.PageRequest; -import com.codingapi.springboot.framework.dto.request.RequestFilter; -import org.springframework.beans.BeanUtils; -import org.springframework.data.domain.Example; - -import javax.persistence.ManyToMany; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; -import javax.persistence.criteria.*; -import java.beans.PropertyDescriptor; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Set; - -public class QueryRequest { - - private final PageRequest request; - private final Class clazz; - - public QueryRequest(PageRequest request, Class clazz) { - this.request = request; - this.clazz = clazz; - } - - public Example getExample() { - RequestFilter requestFilter = request.getRequestFilter(); - if (!requestFilter.hasFilter()) { - return null; - } - Object entity = null; - try { - entity = clazz.getDeclaredConstructor().newInstance(); - } catch (Exception e) { - throw new RuntimeException(e); - } - PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(clazz); - for (PropertyDescriptor descriptor : descriptors) { - String name = descriptor.getName(); - Filter value = requestFilter.getFilter(name); - if (value != null) { - try { - descriptor.getWriteMethod().invoke(entity, value.getFilterValue(descriptor.getPropertyType())); - } catch (Exception e) { - } - } - } - return (Example) Example.of(entity); - } - - - private List getClazzProperties() { - return getClazzProperties(clazz); - } - - private List getClazzProperties(Class clazz) { - List properties = new ArrayList<>(); - PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(clazz); - for (PropertyDescriptor descriptor : descriptors) { - Class propertyType = descriptor.getPropertyType(); - String name = descriptor.getName(); - if (propertyType.getPackage() != null && !propertyType.getPackage().getName().startsWith("java")) { - List childProperties = getClazzProperties(propertyType); - for (String child : childProperties) { - properties.add(name + "." + child); - } - } - properties.add(name); - } - return properties; - } - - - public List getOrder(Root root, CriteriaBuilder criteriaBuilder) { - List orderList = new ArrayList<>(); - request.getSort().forEach(sort -> { - if (sort.getDirection().isAscending()) { - orderList.add(criteriaBuilder.asc(root.get(sort.getProperty()))); - } else { - orderList.add(criteriaBuilder.asc(root.get(sort.getProperty()))); - } - }); - return orderList; - } - - private Path getPathByKey(Root root, String key) { - String[] keys = key.split("\\."); - if (keys.length > 1) { - String lastKey = keys[keys.length-1]; - From current = root; - for (int i=0;i< keys.length-1;i++) { - String item = keys[i]; - Set joins = current.getJoins(); - for (Join join : joins) { - if (join.getModel().getBindableJavaType().getSimpleName().equalsIgnoreCase(item)) { - current = join; - } - } - } - return current.get(lastKey); - } else { - return root.get(key); - } - } - - - private Predicate toPredicate(Filter filter, CriteriaBuilder criteriaBuilder, Root root, List properties) { - String key = filter.getKey(); - if (filter.isAndFilters() || filter.isOrFilters() || properties.contains(key)) { - - Path path = getPathByKey(root, key); - - if (filter.isEqual()) { - return criteriaBuilder.equal(path, filter.getValue()[0]); - } - - if (filter.isLike()) { - String matchValue = (String) filter.getValue()[0]; - return criteriaBuilder.like(path, "%" + matchValue + "%"); - } - - if (filter.isBetween()) { - Object value1 = filter.getValue()[0]; - Object value2 = filter.getValue()[2]; - if (value1 instanceof Integer && value2 instanceof Integer) { - return criteriaBuilder.between(path, (Integer) value1, (Integer) value2); - } - - if (value1 instanceof Long && value2 instanceof Long) { - return criteriaBuilder.between(path, (Long) value1, (Long) value2); - } - - if (value1 instanceof Date && value2 instanceof Date) { - return criteriaBuilder.between(path, (Date) value1, (Date) value2); - } - } - - if (filter.isGreaterThan()) { - Object value = filter.getValue()[0]; - if (value instanceof Integer) { - return criteriaBuilder.greaterThan(path, (Integer) value); - } - if (value instanceof Long) { - return criteriaBuilder.greaterThan(path, (Long) value); - } - if (value instanceof Date) { - return criteriaBuilder.greaterThan(path, (Date) value); - } - } - - if (filter.isGreaterThanEqual()) { - Object value = filter.getValue()[0]; - if (value instanceof Integer) { - return criteriaBuilder.greaterThanOrEqualTo(path, (Integer) value); - } - if (value instanceof Long) { - return criteriaBuilder.greaterThanOrEqualTo(path, (Long) value); - } - if (value instanceof Date) { - return criteriaBuilder.greaterThanOrEqualTo(path, (Date) value); - } - } - - if (filter.isLessThan()) { - Object value = filter.getValue()[0]; - if (value instanceof Integer) { - return criteriaBuilder.lessThan(path, (Integer) value); - } - if (value instanceof Long) { - return criteriaBuilder.lessThan(path, (Long) value); - } - if (value instanceof Date) { - return criteriaBuilder.lessThan(path, (Date) value); - } - } - - if (filter.isLessThanEqual()) { - Object value = filter.getValue()[0]; - if (value instanceof Integer) { - return criteriaBuilder.lessThanOrEqualTo(path, (Integer) value); - } - if (value instanceof Long) { - return criteriaBuilder.lessThanOrEqualTo(path, (Long) value); - } - if (value instanceof Date) { - return criteriaBuilder.lessThanOrEqualTo(path, (Date) value); - } - } - - if (filter.isIn()) { - Object[] value = filter.getValue(); - CriteriaBuilder.In in = criteriaBuilder.in(path); - for (Object item : value) { - in.value(item); - } - return in; - } - - if (filter.isOrFilters()) { - Filter[] orFilters = (Filter[]) filter.getValue(); - List orPredicates = new ArrayList<>(); - for (Filter orFilter : orFilters) { - Predicate predicate = toPredicate(orFilter, criteriaBuilder, root, properties); - if (predicate != null) { - orPredicates.add(predicate); - } - } - return criteriaBuilder.or(orPredicates.toArray(new Predicate[0])); - } - - if (filter.isAndFilters()) { - Filter[] orFilters = (Filter[]) filter.getValue(); - List addPredicates = new ArrayList<>(); - for (Filter orFilter : orFilters) { - Predicate predicate = toPredicate(orFilter, criteriaBuilder, root, properties); - if (predicate != null) { - addPredicates.add(predicate); - } - } - return criteriaBuilder.and(addPredicates.toArray(new Predicate[0])); - } - } - return null; - } - - - private void fetchJoins(From root, Class clazz) { - Field[] fields = clazz.getDeclaredFields(); - for (Field field : fields) { - if (field.getAnnotation(OneToOne.class) != null) { - Join join = root.join(field.getName(), JoinType.INNER); - this.fetchJoins(join, field.getType()); - } - if (field.getAnnotation(ManyToOne.class) != null) { - Join join = root.join(field.getName(), JoinType.INNER); - this.fetchJoins(join, field.getType()); - } - if (field.getAnnotation(OneToMany.class) != null) { - Join join = root.join(field.getName(), JoinType.INNER); - this.fetchJoins(join, field.getType()); - } - if (field.getAnnotation(ManyToMany.class) != null) { - Join join = root.join(field.getName(), JoinType.INNER); - this.fetchJoins(join, field.getType()); - } - } - } - - public List getPredicate(Root root, CriteriaBuilder criteriaBuilder) { - List predicates = new ArrayList<>(); - List properties = getClazzProperties(); - RequestFilter requestFilter = request.getRequestFilter(); - this.fetchJoins(root, clazz); - for (Filter filter : requestFilter.getFilters()) { - Predicate predicate = toPredicate(filter, criteriaBuilder, root, properties); - if (predicate != null) { - predicates.add(predicate); - } - } - return predicates; - } -} \ No newline at end of file diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/MenuRepositoryTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/MenuRepositoryTest.java new file mode 100644 index 00000000..00cfe07c --- /dev/null +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/MenuRepositoryTest.java @@ -0,0 +1,60 @@ +package com.codingapi.springboot.fast; + +import com.codingapi.springboot.fast.entity.Menu; +import com.codingapi.springboot.fast.repository.MenuRepository; +import com.codingapi.springboot.framework.dto.request.PageRequest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Page; + +import java.util.Arrays; +import java.util.List; + +@SpringBootTest +public class MenuRepositoryTest { + + @Autowired + private MenuRepository menuRepository; + + @Test + void test() { + + Menu parent = new Menu(); + parent.setName("parent"); + parent = menuRepository.save(parent); + + + Menu menu = new Menu(); + menu.setName("test"); + menu.setParent(parent); + menuRepository.save(menu); + + + PageRequest request = PageRequest.of(0, 10); + request.addFilter("parent.id", 1); + Page page = menuRepository.pageRequest(request); + System.out.println(page.getTotalElements()); + System.out.println(page.getContent()); + } + + + @Test + void test1() { + + Menu parent = new Menu(); + parent.setName("parent"); + parent = menuRepository.save(parent); + + + Menu menu = new Menu(); + menu.setName("test"); + menu.setParent(parent); + menuRepository.save(menu); + + + List menuList = menuRepository.dynamicListQuery("from Menu m where m.id in (?1)", Arrays.asList(1, 2)); + System.out.println(menuList); + } +} + diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Menu.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Menu.java new file mode 100644 index 00000000..efe7424c --- /dev/null +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/entity/Menu.java @@ -0,0 +1,22 @@ +package com.codingapi.springboot.fast.entity; + +import javax.persistence.*; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Setter +@Getter +@Entity +@ToString +public class Menu { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + private String name; + + @ManyToOne + private Menu parent; + +} diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/MenuRepository.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/MenuRepository.java new file mode 100644 index 00000000..ed271005 --- /dev/null +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/repository/MenuRepository.java @@ -0,0 +1,9 @@ +package com.codingapi.springboot.fast.repository; + +import com.codingapi.springboot.fast.entity.Menu; +import com.codingapi.springboot.fast.jpa.repository.FastRepository; + +public interface MenuRepository extends FastRepository { + + +} diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java index 19a9282a..cb650b2e 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java @@ -1,12 +1,12 @@ package com.codingapi.springboot.framework.dto.request; -import javax.servlet.http.HttpServletRequest; import lombok.Getter; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import javax.servlet.http.HttpServletRequest; import java.util.Optional; public class PageRequest extends org.springframework.data.domain.PageRequest { @@ -44,6 +44,7 @@ public PageRequest() { public void setCurrent(int current) { this.current = current > 0 ? current - 1 : 0; + this.requestFilter.deleteFilter("current"); } public String getParameter(String key) { @@ -91,6 +92,7 @@ public int getPageSize() { public void setPageSize(int pageSize) { this.pageSize = pageSize; + this.requestFilter.deleteFilter("pageSize"); } @Override diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java index 5c4cf90e..7ea9232b 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java @@ -1,6 +1,5 @@ package com.codingapi.springboot.framework.dto.request; - import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; @@ -98,4 +97,9 @@ public boolean hasFilter() { public Filter getFilter(String name) { return this.filterMap.get(name); } + + public void deleteFilter(String current) { + this.filterMap.remove(current); + this.filterList.removeIf(item -> item.getKey().equals(current)); + } } From 3f1fadcbd8a0ac0f90c178792066eb9ad84602a8 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Fri, 5 Jan 2024 18:16:12 +0800 Subject: [PATCH 028/101] update 2.7.7 --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- .../springboot/fast/jpa/repository/DynamicRequest.java | 2 -- springboot-starter-security-jwt/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index fce76f60..b083a83f 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.7.7.dev + 2.7.7 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 553bbcd3..f4cc7071 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.7.dev + 2.7.7 4.0.0 diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRequest.java index 59035275..ef91f3c6 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRequest.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRequest.java @@ -50,8 +50,6 @@ public String getHql() { } } - System.out.println(hql.toString()); - System.out.println(params); return hql.toString(); } diff --git a/springboot-starter-security-jwt/pom.xml b/springboot-starter-security-jwt/pom.xml index e349a1b9..29babada 100644 --- a/springboot-starter-security-jwt/pom.xml +++ b/springboot-starter-security-jwt/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.7.7.dev + 2.7.7 springboot-starter-security-jwt diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 00ace6d5..6422cce2 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.7.7.dev + 2.7.7 springboot-starter From 10eecc5d770cbc20acd13d1b2e9bf11eb5555b50 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Fri, 5 Jan 2024 18:27:24 +0800 Subject: [PATCH 029/101] update 2.7.8 --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-security-jwt/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index b083a83f..a38c1d6f 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.7.7 + 2.7.8 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index f4cc7071..8efb653a 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.7 + 2.7.8 4.0.0 diff --git a/springboot-starter-security-jwt/pom.xml b/springboot-starter-security-jwt/pom.xml index 29babada..7ba2c2bd 100644 --- a/springboot-starter-security-jwt/pom.xml +++ b/springboot-starter-security-jwt/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.7.7 + 2.7.8 springboot-starter-security-jwt diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 6422cce2..62475341 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.7.7 + 2.7.8 springboot-starter From 5bf61432ac77e37645a8926cb174037fbee30883 Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Sat, 6 Jan 2024 17:52:48 +0800 Subject: [PATCH 030/101] add SearchRequest --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- ...micRequest.java => DynamicSQLBuilder.java} | 13 +- ...xampleRequest.java => ExampleBuilder.java} | 7 +- .../fast/jpa/repository/FastRepository.java | 19 ++- .../fast/jpa/repository/SearchRequest.java | 154 ++++++++++++++++++ .../springboot/fast/DemoRepositoryTest.java | 14 +- springboot-starter-security-jwt/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../framework/dto/request/Filter.java | 3 +- .../framework/dto/request/PageRequest.java | 55 +------ .../framework/dto/request/RequestFilter.java | 17 +- .../dto/request/PageRequestTest.java | 2 +- .../query/test/DemoRepositoryTest.java | 2 +- 14 files changed, 210 insertions(+), 84 deletions(-) rename springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/{DynamicRequest.java => DynamicSQLBuilder.java} (94%) rename springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/{ExampleRequest.java => ExampleBuilder.java} (92%) create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SearchRequest.java diff --git a/pom.xml b/pom.xml index a38c1d6f..58c1d5f5 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.7.8 + 2.7.9.dev https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 8efb653a..94d34948 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.8 + 2.7.9.dev 4.0.0 diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java similarity index 94% rename from springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRequest.java rename to springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java index ef91f3c6..06705ad8 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRequest.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java @@ -3,13 +3,18 @@ import com.codingapi.springboot.framework.dto.request.Filter; import com.codingapi.springboot.framework.dto.request.PageRequest; import com.codingapi.springboot.framework.dto.request.RequestFilter; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Sort; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -public class DynamicRequest { +/** + * 动态条件查询组装 + */ +@Slf4j +class DynamicSQLBuilder { private final PageRequest request; private final Class clazz; @@ -17,13 +22,13 @@ public class DynamicRequest { private final List params = new ArrayList<>(); private int paramIndex = 1; - public DynamicRequest(PageRequest request, Class clazz) { + public DynamicSQLBuilder(PageRequest request, Class clazz) { this.request = request; this.clazz = clazz; } - public String getHql() { + public String getHQL() { StringBuilder hql = new StringBuilder("FROM " + clazz.getSimpleName() + " WHERE "); RequestFilter requestFilter = request.getRequestFilter(); if (requestFilter.hasFilter()) { @@ -50,6 +55,8 @@ public String getHql() { } } + log.debug("hql:{}", hql); + log.debug("params:{}", params); return hql.toString(); } diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/ExampleRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/ExampleBuilder.java similarity index 92% rename from springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/ExampleRequest.java rename to springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/ExampleBuilder.java index 25ee2ed2..6b72dec3 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/ExampleRequest.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/ExampleBuilder.java @@ -8,12 +8,15 @@ import java.beans.PropertyDescriptor; -public class ExampleRequest { +/** + * Example组装 + */ +class ExampleBuilder { private final PageRequest request; private final Class clazz; - public ExampleRequest(PageRequest request, Class clazz) { + public ExampleBuilder(PageRequest request, Class clazz) { this.request = request; this.clazz = clazz; } diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java index 4b55b787..523d721b 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java @@ -7,14 +7,19 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.NoRepositoryBean; +/** + * 更强大的Repository对象 + * @param + * @param + */ @NoRepositoryBean public interface FastRepository extends JpaRepository, JpaSpecificationExecutor, DynamicRepository { default Page findAll(PageRequest request) { if (request.hasFilter()) { Class clazz = getDomainClass(); - ExampleRequest exampleRequest = new ExampleRequest(request, clazz); - return findAll(exampleRequest.getExample(), request); + ExampleBuilder exampleBuilder = new ExampleBuilder(request, clazz); + return findAll(exampleBuilder.getExample(), request); } return findAll((org.springframework.data.domain.PageRequest) request); } @@ -30,10 +35,16 @@ default Class getDomainClass() { default Page pageRequest(PageRequest request) { if (request.hasFilter()) { Class clazz = getDomainClass(); - DynamicRequest dynamicRequest = new DynamicRequest(request,clazz); - return dynamicPageQuery(dynamicRequest.getHql(), request, dynamicRequest.getParams()); + DynamicSQLBuilder dynamicSQLBuilder = new DynamicSQLBuilder(request, clazz); + return dynamicPageQuery(dynamicSQLBuilder.getHQL(), request, dynamicSQLBuilder.getParams()); } return findAll((org.springframework.data.domain.PageRequest) request); } + + default Page searchRequest(SearchRequest request) { + Class clazz = getDomainClass(); + return pageRequest(request.toPageRequest(clazz)); + } + } diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SearchRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SearchRequest.java new file mode 100644 index 00000000..866b8870 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SearchRequest.java @@ -0,0 +1,154 @@ +package com.codingapi.springboot.fast.jpa.repository; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.codingapi.springboot.framework.dto.request.PageRequest; +import com.codingapi.springboot.framework.dto.request.Relation; +import javax.servlet.http.HttpServletRequest; +import org.springframework.data.domain.Sort; +import org.springframework.util.StringUtils; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Enumeration; +import java.util.List; +import java.util.stream.Collectors; + +/** + * HttpServletRequest 请求参数解析成 PageRequest对象 + */ +public class SearchRequest { + + private int current; + private int pageSize; + + private final HttpServletRequest request; + + private final List removeKeys = new ArrayList<>(); + + private final PageRequest pageRequest; + + public SearchRequest() { + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); + this.request = attributes.getRequest(); + this.pageRequest = new PageRequest(); + } + + public void setCurrent(int current) { + this.current = current - 1; + this.removeKeys.add("current"); + } + + public void setPageSize(int pageSize) { + this.pageSize = pageSize; + this.removeKeys.add("pageSize"); + } + + private String decode(String value) { + return new String(Base64.getDecoder().decode(value)); + } + + + static class ClassContent { + + private final Class clazz; + private final PageRequest pageRequest; + + public ClassContent(Class clazz, PageRequest pageRequest) { + this.clazz = clazz; + this.pageRequest = pageRequest; + } + + public void addFilter(String key, String value) { + Class keyClass = getKeyType(key); + Object v = JSON.parseObject(value, keyClass); + pageRequest.addFilter(key, Relation.EQUAL, v); + } + + public void addFilter(String key, List value) { + Class keyClass = getKeyType(key); + pageRequest.addFilter(key, Relation.IN, value.stream() + .map(v -> JSON.parseObject(v, keyClass)) + .toArray() + ); + } + + + private Class getKeyType(String key) { + String[] keys = key.split("\\."); + Class keyClass = clazz; + for (String k : keys) { + Field[] fields = keyClass.getDeclaredFields(); + for (Field field : fields) { + if (field.getName().equals(k)) { + keyClass = field.getType(); + break; + } + } + } + return keyClass; + } + + } + + PageRequest toPageRequest(Class clazz) { + pageRequest.setCurrent(current); + pageRequest.setPageSize(pageSize); + + ClassContent content = new ClassContent(clazz, pageRequest); + + String sort = request.getParameter("sort"); + if (StringUtils.hasLength(sort)) { + sort = decode(sort); + if (JSON.isValid(sort)) { + removeKeys.add("sort"); + JSONObject jsonObject = JSON.parseObject(sort); + for (String key : jsonObject.keySet()) { + String value = jsonObject.getString(key); + if ("ascend".equals(value)) { + pageRequest.addSort(Sort.by(key).ascending()); + } else { + pageRequest.addSort(Sort.by(key).descending()); + } + } + } + } + + + String filter = request.getParameter("filter"); + if (StringUtils.hasLength(filter)) { + filter = decode(filter); + if (JSON.isValid(filter)) { + removeKeys.add("filter"); + JSONObject jsonObject = JSON.parseObject(filter); + for (String key : jsonObject.keySet()) { + JSONArray value = jsonObject.getJSONArray(key); + if (value != null && !value.isEmpty()) { + List values = value.stream().map(Object::toString).collect(Collectors.toList()); + content.addFilter(key, values); + } + } + } + } + + Enumeration enumeration = request.getParameterNames(); + while (enumeration.hasMoreElements()){ + String key = enumeration.nextElement(); + if (!removeKeys.contains(key)) { + String value = request.getParameter(key); + if (StringUtils.hasLength(value)) { + content.addFilter(key, value); + } + } + } + + + return pageRequest; + } + + +} diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java index 515c5550..9b2ec82a 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java @@ -47,7 +47,7 @@ void findAll() { demoRepository.save(demo2); PageRequest request = new PageRequest(); - request.setCurrent(1); + request.setCurrent(0); request.setPageSize(10); request.addFilter("name", "123"); @@ -68,9 +68,9 @@ void pageRequest() { demoRepository.save(demo2); PageRequest request = new PageRequest(); - request.setCurrent(1); + request.setCurrent(0); request.setPageSize(10); - request.andFilter("name", Relation.LIKE, "%2%"); + request.addFilter("name", Relation.LIKE, "%2%"); Page page = demoRepository.pageRequest(request); assertEquals(1, page.getTotalElements()); @@ -89,10 +89,10 @@ void customInSearch() { demoRepository.save(demo2); PageRequest request = new PageRequest(); - request.setCurrent(1); + request.setCurrent(0); request.setPageSize(10); - request.andFilter("id", Relation.IN, 1, 2, 3); + request.addFilter("id", Relation.IN, 1, 2, 3); Page page = demoRepository.pageRequest(request); log.info("demo:{}", page.getContent()); @@ -112,7 +112,7 @@ void customOrSearch() { demoRepository.save(demo2); PageRequest request = new PageRequest(); - request.setCurrent(1); + request.setCurrent(0); request.setPageSize(10); @@ -167,7 +167,7 @@ void sortQuery() { demoRepository.save(demo2); PageRequest request = new PageRequest(); - request.setCurrent(1); + request.setCurrent(0); request.setPageSize(10); request.addSort(Sort.by("id").descending()); diff --git a/springboot-starter-security-jwt/pom.xml b/springboot-starter-security-jwt/pom.xml index 7ba2c2bd..f77ce3af 100644 --- a/springboot-starter-security-jwt/pom.xml +++ b/springboot-starter-security-jwt/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.7.8 + 2.7.9.dev springboot-starter-security-jwt diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 62475341..a1480fcc 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.7.8 + 2.7.9.dev springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java index 0acb1477..55c901b8 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java @@ -1,6 +1,5 @@ package com.codingapi.springboot.framework.dto.request; - import lombok.Getter; import lombok.Setter; @@ -26,7 +25,7 @@ public Filter(String key, Object... value) { } public Filter(String key, Filter... value) { - this(key, null, value); + this(key, null, value); } public static Filter as(String key, Relation relation, Object... value) { diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java index cb650b2e..c81cec33 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java @@ -1,25 +1,25 @@ package com.codingapi.springboot.framework.dto.request; import lombok.Getter; +import lombok.Setter; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; -import javax.servlet.http.HttpServletRequest; import java.util.Optional; public class PageRequest extends org.springframework.data.domain.PageRequest { @Getter + @Setter private int current; + + @Setter + @Getter private int pageSize; @Getter private final RequestFilter requestFilter = new RequestFilter(); - @Getter - private HttpServletRequest servletRequest; private org.springframework.data.domain.PageRequest pageRequest; @@ -28,13 +28,6 @@ public PageRequest(int current, int pageSize, Sort sort) { this.current = current; this.pageSize = pageSize; this.pageRequest = org.springframework.data.domain.PageRequest.of(current, pageSize, sort); - - try { - ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); - this.servletRequest = attributes.getRequest(); - requestFilter.syncParameter(servletRequest); - } catch (Exception e) { - } } @@ -42,29 +35,6 @@ public PageRequest() { this(0, 20, Sort.unsorted()); } - public void setCurrent(int current) { - this.current = current > 0 ? current - 1 : 0; - this.requestFilter.deleteFilter("current"); - } - - public String getParameter(String key) { - return servletRequest.getParameter(key); - } - - public String getParameter(String key, String defaultValue) { - String result = servletRequest.getParameter(key); - return result == null ? defaultValue : result; - } - - public int getIntParameter(String key) { - return Integer.parseInt(servletRequest.getParameter(key)); - } - - public int getIntParameter(String key, int defaultValue) { - String result = servletRequest.getParameter(key); - return result == null ? defaultValue : Integer.parseInt(result); - } - public String getStringFilter(String key) { return requestFilter.getStringFilter(key); } @@ -85,15 +55,6 @@ public boolean hasFilter() { return requestFilter.hasFilter(); } - @Override - public int getPageSize() { - return pageSize; - } - - public void setPageSize(int pageSize) { - this.pageSize = pageSize; - this.requestFilter.deleteFilter("pageSize"); - } @Override public Sort getSort() { @@ -164,7 +125,7 @@ public void addSort(Sort sort) { } } - public PageRequest andFilter(String key, Relation relation, Object... value) { + public PageRequest addFilter(String key, Relation relation, Object... value) { requestFilter.addFilter(key, relation, value); return this; } @@ -174,8 +135,8 @@ public PageRequest addFilter(String key, Object... value) { return this; } - public PageRequest andFilter(Filter... value) { - requestFilter.andFilters(value); + public PageRequest andFilter(Filter... filters) { + requestFilter.andFilters(filters); return this; } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java index 7ea9232b..2e1ac3c6 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java @@ -2,8 +2,10 @@ import org.springframework.util.StringUtils; -import javax.servlet.http.HttpServletRequest; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class RequestFilter { @@ -40,17 +42,6 @@ public void pushFilter(Filter filter) { filterMap.put(filter.getKey(), filter); } - public void syncParameter(HttpServletRequest servletRequest) { - Enumeration enumeration = servletRequest.getParameterNames(); - while (enumeration.hasMoreElements()) { - String key = enumeration.nextElement(); - String value = servletRequest.getParameter(key); - if (StringUtils.hasText(value)) { - addFilter(key, value); - } - } - } - public String getStringFilter(String key) { Filter filter = filterMap.get(key); diff --git a/springboot-starter/src/test/java/com/codingapi/springboot/framework/dto/request/PageRequestTest.java b/springboot-starter/src/test/java/com/codingapi/springboot/framework/dto/request/PageRequestTest.java index e3584bfa..93028dd8 100644 --- a/springboot-starter/src/test/java/com/codingapi/springboot/framework/dto/request/PageRequestTest.java +++ b/springboot-starter/src/test/java/com/codingapi/springboot/framework/dto/request/PageRequestTest.java @@ -9,7 +9,7 @@ class PageRequestTest { @Test void test(){ PageRequest pageRequest = new PageRequest(); - pageRequest.setCurrent(2); + pageRequest.setCurrent(1); pageRequest.setPageSize(10); assertEquals(pageRequest.getCurrent(),1); diff --git a/springboot-starter/src/test/java/com/codingapi/springboot/framework/query/test/DemoRepositoryTest.java b/springboot-starter/src/test/java/com/codingapi/springboot/framework/query/test/DemoRepositoryTest.java index a35c577f..2b787c96 100644 --- a/springboot-starter/src/test/java/com/codingapi/springboot/framework/query/test/DemoRepositoryTest.java +++ b/springboot-starter/src/test/java/com/codingapi/springboot/framework/query/test/DemoRepositoryTest.java @@ -67,7 +67,7 @@ void sort(){ demoRepository.save(demo2); PageRequest request = new PageRequest(); - request.setCurrent(1); + request.setCurrent(0); request.setPageSize(10); request.addSort(Sort.by("id").descending()); From aafe75586a7097589536ba06ca988d84d1c213c4 Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Sat, 6 Jan 2024 18:14:41 +0800 Subject: [PATCH 031/101] add SearchRequest --- .../fast/jpa/repository/SearchRequest.java | 25 +++++++++++++++++++ .../framework/dto/request/PageRequest.java | 4 +++ .../framework/dto/request/RequestFilter.java | 6 ++--- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SearchRequest.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SearchRequest.java index 866b8870..40d002ba 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SearchRequest.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SearchRequest.java @@ -3,6 +3,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; +import com.codingapi.springboot.framework.dto.request.Filter; import com.codingapi.springboot.framework.dto.request.PageRequest; import com.codingapi.springboot.framework.dto.request.Relation; import javax.servlet.http.HttpServletRequest; @@ -53,6 +54,30 @@ private String decode(String value) { } + public void addSort(Sort sort) { + pageRequest.addSort(sort); + } + + public void removeFilter(String key) { + pageRequest.removeFilter(key); + } + + public PageRequest addFilter(String key, Relation relation, Object... value) { + return pageRequest.addFilter(key, relation, value); + } + + public PageRequest addFilter(String key, Object... value) { + return pageRequest.addFilter(key, value); + } + + public PageRequest andFilter(Filter... filters) { + return pageRequest.andFilter(filters); + } + + public PageRequest orFilters(Filter... filters) { + return pageRequest.orFilters(filters); + } + static class ClassContent { private final Class clazz; diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java index c81cec33..ff5b3df5 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java @@ -125,6 +125,10 @@ public void addSort(Sort sort) { } } + public void removeFilter(String key) { + requestFilter.removeFilter(key); + } + public PageRequest addFilter(String key, Relation relation, Object... value) { requestFilter.addFilter(key, relation, value); return this; diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java index 2e1ac3c6..0773eac8 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/RequestFilter.java @@ -89,8 +89,8 @@ public Filter getFilter(String name) { return this.filterMap.get(name); } - public void deleteFilter(String current) { - this.filterMap.remove(current); - this.filterList.removeIf(item -> item.getKey().equals(current)); + public void removeFilter(String key) { + this.filterMap.remove(key); + this.filterList.removeIf(item -> item.getKey().equals(key)); } } From 216fc20dd804b4798bb5bda04abad24d503219f6 Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Sat, 6 Jan 2024 18:21:00 +0800 Subject: [PATCH 032/101] add SearchRequest --- .../fast/jpa/repository/FastRepository.java | 1 + .../framework/dto/request}/SearchRequest.java | 13 +++++-------- 2 files changed, 6 insertions(+), 8 deletions(-) rename {springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository => springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request}/SearchRequest.java (92%) diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java index 523d721b..d3e2deb8 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java @@ -1,6 +1,7 @@ package com.codingapi.springboot.fast.jpa.repository; import com.codingapi.springboot.framework.dto.request.PageRequest; +import com.codingapi.springboot.framework.dto.request.SearchRequest; import org.springframework.core.ResolvableType; import org.springframework.data.domain.Page; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SearchRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java similarity index 92% rename from springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SearchRequest.java rename to springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java index 40d002ba..364a2dc8 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SearchRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java @@ -1,17 +1,14 @@ -package com.codingapi.springboot.fast.jpa.repository; +package com.codingapi.springboot.framework.dto.request; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; -import com.codingapi.springboot.framework.dto.request.Filter; -import com.codingapi.springboot.framework.dto.request.PageRequest; -import com.codingapi.springboot.framework.dto.request.Relation; -import javax.servlet.http.HttpServletRequest; import org.springframework.data.domain.Sort; import org.springframework.util.StringUtils; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Base64; @@ -20,7 +17,7 @@ import java.util.stream.Collectors; /** - * HttpServletRequest 请求参数解析成 PageRequest对象 + * HttpServletRequest 请求参数解析成 PageRequest对象 */ public class SearchRequest { @@ -120,7 +117,7 @@ private Class getKeyType(String key) { } - PageRequest toPageRequest(Class clazz) { + public PageRequest toPageRequest(Class clazz) { pageRequest.setCurrent(current); pageRequest.setPageSize(pageSize); @@ -161,7 +158,7 @@ PageRequest toPageRequest(Class clazz) { } Enumeration enumeration = request.getParameterNames(); - while (enumeration.hasMoreElements()){ + while (enumeration.hasMoreElements()) { String key = enumeration.nextElement(); if (!removeKeys.contains(key)) { String value = request.getParameter(key); From a39998b9f41603f6ca4b52f7f4e3d92af994ca92 Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Sat, 6 Jan 2024 21:59:30 +0800 Subject: [PATCH 033/101] add SortRepository --- .../fast/jpa/repository/SortRepository.java | 35 +++++++++++++++++++ .../springboot/framework/domain/ISort.java | 8 +++++ .../framework/dto/request/SortRequest.java | 14 ++++++++ 3 files changed, 57 insertions(+) create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SortRepository.java create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/domain/ISort.java create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SortRequest.java diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SortRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SortRepository.java new file mode 100644 index 00000000..9bbbeb9b --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SortRepository.java @@ -0,0 +1,35 @@ +package com.codingapi.springboot.fast.jpa.repository; + +import com.codingapi.springboot.framework.domain.ISort; +import com.codingapi.springboot.framework.dto.request.SortRequest; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.NoRepositoryBean; + +import java.util.ArrayList; +import java.util.List; + +@NoRepositoryBean +public interface SortRepository extends JpaRepository { + + + default void reSort(SortRequest request) { + if (request != null && !request.getIds().isEmpty()) { + List list = new ArrayList<>(); + int minSort = 0; + for (Object objectId : request.getIds()) { + ID id = (ID) objectId; + T t = getReferenceById(id); + if (t.getSort() != null && t.getSort() < minSort) { + minSort = t.getSort(); + } + list.add(t); + } + for (T t : list) { + t.setSort(minSort++); + } + saveAll(list); + } + } + + +} diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/domain/ISort.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/domain/ISort.java new file mode 100644 index 00000000..f9efc10a --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/domain/ISort.java @@ -0,0 +1,8 @@ +package com.codingapi.springboot.framework.domain; + +public interface ISort { + + void setSort(Integer sort); + + Integer getSort(); +} diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SortRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SortRequest.java new file mode 100644 index 00000000..25d5a9b9 --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SortRequest.java @@ -0,0 +1,14 @@ +package com.codingapi.springboot.framework.dto.request; + +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Setter +@Getter +public class SortRequest { + + private List ids; + +} From b872ea0008b5a0e0ab7e734369631f531df34057 Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Sat, 6 Jan 2024 22:06:35 +0800 Subject: [PATCH 034/101] add SortRepository --- .../springboot/fast/jpa/repository/SortRepository.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SortRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SortRepository.java index 9bbbeb9b..a4e64852 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SortRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SortRepository.java @@ -15,12 +15,14 @@ public interface SortRepository extends JpaRepository list = new ArrayList<>(); - int minSort = 0; + int minSort = Integer.MAX_VALUE; for (Object objectId : request.getIds()) { ID id = (ID) objectId; T t = getReferenceById(id); if (t.getSort() != null && t.getSort() < minSort) { minSort = t.getSort(); + }else{ + minSort = 0; } list.add(t); } From 9d5357575045964392f32fec99a457134f78ad73 Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Sat, 6 Jan 2024 22:11:36 +0800 Subject: [PATCH 035/101] add SortRepository --- .../springboot/fast/jpa/repository/SortRepository.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SortRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SortRepository.java index a4e64852..a47a0650 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SortRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/SortRepository.java @@ -19,10 +19,12 @@ default void reSort(SortRequest request) { for (Object objectId : request.getIds()) { ID id = (ID) objectId; T t = getReferenceById(id); - if (t.getSort() != null && t.getSort() < minSort) { - minSort = t.getSort(); - }else{ + if (t.getSort() == null) { minSort = 0; + } else { + if (t.getSort() < minSort) { + minSort = t.getSort(); + } } list.add(t); } From 7b2d24715cf010f99ce86b2c8eb7e469cdb3495b Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Wed, 10 Jan 2024 10:50:18 +0800 Subject: [PATCH 036/101] add JPAQuery#pageQuery(Class clazz, String sql, PageRequest pageRequest, Object... params) --- .../main/java/com/codingapi/springboot/fast/jpa/JPAQuery.java | 4 ++++ .../springboot/fast/jpa/repository/DynamicRepository.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQuery.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQuery.java index 00d64c2a..6342af68 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQuery.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQuery.java @@ -24,6 +24,10 @@ public List listQuery(Class clazz, String sql, Object... params) { return query.getResultList(); } + public Page pageQuery(Class clazz, String sql, PageRequest pageRequest, Object... params) { + return pageQuery(clazz,sql,"select count(1) " + sql,pageRequest,params); + } + public Page pageQuery(Class clazz, String sql, String countSql, PageRequest pageRequest, Object... params) { TypedQuery query = entityManager.createQuery(sql, clazz); if (params != null) { diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRepository.java index 186f45a1..992f8ab2 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRepository.java @@ -31,7 +31,7 @@ default Page dynamicPageQuery(String sql, String countSql, PageRequest reques } default Page dynamicPageQuery(String sql, PageRequest request, Object... params) { - return (Page) JpaQueryContext.getInstance().getJPAQuery().pageQuery(getEntityClass(), sql, "select count(1) " + sql, request, params); + return (Page) JpaQueryContext.getInstance().getJPAQuery().pageQuery(getEntityClass(), sql, request, params); } default Page dynamicPageQuery(Class clazz, String sql, String countSql, PageRequest request, Object... params) { From 94ffabc4f48a8f33731f12753e0ce4c0016feab8 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Thu, 11 Jan 2024 11:15:14 +0800 Subject: [PATCH 037/101] update 2.7.9 --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-security-jwt/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 58c1d5f5..68850240 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.7.9.dev + 2.7.9 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 94d34948..050b96ec 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.9.dev + 2.7.9 4.0.0 diff --git a/springboot-starter-security-jwt/pom.xml b/springboot-starter-security-jwt/pom.xml index f77ce3af..201790ef 100644 --- a/springboot-starter-security-jwt/pom.xml +++ b/springboot-starter-security-jwt/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.7.9.dev + 2.7.9 springboot-starter-security-jwt diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index a1480fcc..415f2afa 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.7.9.dev + 2.7.9 springboot-starter From 592a579251e550a4a4d47707c9c1ac3d0792393a Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Mon, 29 Jan 2024 10:43:36 +0800 Subject: [PATCH 038/101] #38 --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-security-jwt/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../framework/dto/request/SearchRequest.java | 11 +++++++++-- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 68850240..7c4fe039 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.7.9 + 2.7.10.dev https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 050b96ec..276a40f5 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.9 + 2.7.10.dev 4.0.0 diff --git a/springboot-starter-security-jwt/pom.xml b/springboot-starter-security-jwt/pom.xml index 201790ef..70daff27 100644 --- a/springboot-starter-security-jwt/pom.xml +++ b/springboot-starter-security-jwt/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.7.9 + 2.7.10.dev springboot-starter-security-jwt diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 415f2afa..2904b224 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.7.9 + 2.7.10.dev springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java index 364a2dc8..07c53bdd 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java @@ -87,14 +87,21 @@ public ClassContent(Class clazz, PageRequest pageRequest) { public void addFilter(String key, String value) { Class keyClass = getKeyType(key); - Object v = JSON.parseObject(value, keyClass); + Object v = parseObject(value, keyClass); pageRequest.addFilter(key, Relation.EQUAL, v); } + private Object parseObject(String value, Class keyClass) { + if(value.getClass().equals(keyClass)) { + return value; + } + return JSON.parseObject(value, keyClass); + } + public void addFilter(String key, List value) { Class keyClass = getKeyType(key); pageRequest.addFilter(key, Relation.IN, value.stream() - .map(v -> JSON.parseObject(v, keyClass)) + .map(v -> parseObject(v, keyClass)) .toArray() ); } From e65a0dfd9fe9d25d1419a55f619a22ceae92d2e5 Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Mon, 29 Jan 2024 10:44:02 +0800 Subject: [PATCH 039/101] fix #38 --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-security-jwt/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 7c4fe039..17d00ac7 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.7.10.dev + 2.7.10 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 276a40f5..dba73ff4 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.10.dev + 2.7.10 4.0.0 diff --git a/springboot-starter-security-jwt/pom.xml b/springboot-starter-security-jwt/pom.xml index 70daff27..73c3f64f 100644 --- a/springboot-starter-security-jwt/pom.xml +++ b/springboot-starter-security-jwt/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.7.10.dev + 2.7.10 springboot-starter-security-jwt diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 2904b224..1cf03f54 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.7.10.dev + 2.7.10 springboot-starter From a0e87cbf2bdd78319ca642d0753d74e02731839a Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Sun, 31 Mar 2024 14:36:19 +0800 Subject: [PATCH 040/101] add security redis support --- README.md | 4 +- ...-jwt.md => springboot-starter-security.md} | 38 +++++++---- pom.xml | 12 ++-- springboot-starter-data-fast/pom.xml | 2 +- .../main/resources/META-INF/spring.factories | 4 -- ...ot.autoconfigure.AutoConfiguration.imports | 3 - .../pom.xml | 17 +++-- .../security/AutoConfiguration.java | 26 +++----- .../configurer/HttpSecurityConfigurer.java | 12 ++-- .../configurer/WebSecurityConfigurer.java | 6 +- .../controller/VersionController.java | 0 .../springboot/security/crypto/AESTools.java | 8 +-- .../crypto/SecurityCryptoConfiguration.java | 9 ++- .../security/dto/request/LoginRequest.java | 0 .../dto/request/LoginRequestContext.java | 0 .../security/dto/response/LoginResponse.java | 0 .../exception/TokenExpiredException.java | 0 .../filter/AuthenticationTokenFilter.java | 0 .../filter/MyAccessDeniedHandler.java | 0 .../filter/MyAuthenticationFilter.java | 22 +++---- .../security/filter/MyLoginFilter.java | 18 ++--- .../security/filter/MyLogoutHandler.java | 0 .../filter/MyLogoutSuccessHandler.java | 0 .../filter/MyUnAuthenticationEntryPoint.java | 0 .../security/filter/SecurityLoginHandler.java | 2 +- .../springboot/security/gateway}/Token.java | 8 +-- .../security/gateway}/TokenContext.java | 2 +- .../security/gateway/TokenGateway.java | 23 +++++++ .../jwt/JWTSecurityConfiguration.java | 35 ++++++++++ .../security/jwt/JWTTokenGatewayImpl.java | 25 +++++++ .../security/jwt/JwtTokenGateway.java | 21 +++--- .../security/jwt/SecurityJWTProperties.java | 35 ++++++++++ .../CodingApiSecurityProperties.java | 43 ++++-------- .../redis/RedisSecurityConfiguration.java | 35 ++++++++++ .../security/redis/RedisTokenGateway.java | 65 +++++++++++++++++++ .../security/redis/RedisTokenGatewayImpl.java | 26 ++++++++ .../redis/SecurityRedisProperties.java | 27 ++++++++ .../main/resources/META-INF/spring.factories | 6 ++ ...ot.autoconfigure.AutoConfiguration.imports | 5 ++ .../security/SecurityJwtApplication.java | 0 .../security/SecurityJwtApplicationTest.java | 0 .../security/controller/DemoController.java | 0 .../springboot/security/jwt/TestVO.java | 0 .../springboot/security/jwt/TokenTest.java | 16 +++-- .../src/test/resources/application.properties | 10 +-- springboot-starter/pom.xml | 2 +- 46 files changed, 420 insertions(+), 147 deletions(-) rename docs/wiki/{springboot-starter-security-jwt.md => springboot-starter-security.md} (79%) delete mode 100644 springboot-starter-security-jwt/src/main/resources/META-INF/spring.factories delete mode 100644 springboot-starter-security-jwt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports rename {springboot-starter-security-jwt => springboot-starter-security}/pom.xml (73%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java (86%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/main/java/com/codingapi/springboot/security/configurer/HttpSecurityConfigurer.java (72%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/main/java/com/codingapi/springboot/security/configurer/WebSecurityConfigurer.java (71%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/main/java/com/codingapi/springboot/security/controller/VersionController.java (100%) rename springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/crypto/MyAES.java => springboot-starter-security/src/main/java/com/codingapi/springboot/security/crypto/AESTools.java (84%) rename springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/crypto/MyCryptoConfiguration.java => springboot-starter-security/src/main/java/com/codingapi/springboot/security/crypto/SecurityCryptoConfiguration.java (69%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/main/java/com/codingapi/springboot/security/dto/request/LoginRequest.java (100%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/main/java/com/codingapi/springboot/security/dto/request/LoginRequestContext.java (100%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/main/java/com/codingapi/springboot/security/dto/response/LoginResponse.java (100%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/main/java/com/codingapi/springboot/security/exception/TokenExpiredException.java (100%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/main/java/com/codingapi/springboot/security/filter/AuthenticationTokenFilter.java (100%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/main/java/com/codingapi/springboot/security/filter/MyAccessDeniedHandler.java (100%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java (77%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java (87%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/main/java/com/codingapi/springboot/security/filter/MyLogoutHandler.java (100%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/main/java/com/codingapi/springboot/security/filter/MyLogoutSuccessHandler.java (100%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/main/java/com/codingapi/springboot/security/filter/MyUnAuthenticationEntryPoint.java (100%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java (89%) rename {springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/jwt => springboot-starter-security/src/main/java/com/codingapi/springboot/security/gateway}/Token.java (91%) rename {springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/jwt => springboot-starter-security/src/main/java/com/codingapi/springboot/security/gateway}/TokenContext.java (91%) create mode 100644 springboot-starter-security/src/main/java/com/codingapi/springboot/security/gateway/TokenGateway.java create mode 100644 springboot-starter-security/src/main/java/com/codingapi/springboot/security/jwt/JWTSecurityConfiguration.java create mode 100644 springboot-starter-security/src/main/java/com/codingapi/springboot/security/jwt/JWTTokenGatewayImpl.java rename springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/jwt/Jwt.java => springboot-starter-security/src/main/java/com/codingapi/springboot/security/jwt/JwtTokenGateway.java (78%) create mode 100644 springboot-starter-security/src/main/java/com/codingapi/springboot/security/jwt/SecurityJWTProperties.java rename springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/properties/SecurityJwtProperties.java => springboot-starter-security/src/main/java/com/codingapi/springboot/security/properties/CodingApiSecurityProperties.java (69%) create mode 100644 springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisSecurityConfiguration.java create mode 100644 springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGateway.java create mode 100644 springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGatewayImpl.java create mode 100644 springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/SecurityRedisProperties.java create mode 100644 springboot-starter-security/src/main/resources/META-INF/spring.factories create mode 100644 springboot-starter-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports rename {springboot-starter-security-jwt => springboot-starter-security}/src/test/java/com/codingapi/springboot/security/SecurityJwtApplication.java (100%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/test/java/com/codingapi/springboot/security/SecurityJwtApplicationTest.java (100%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/test/java/com/codingapi/springboot/security/controller/DemoController.java (100%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/test/java/com/codingapi/springboot/security/jwt/TestVO.java (100%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/test/java/com/codingapi/springboot/security/jwt/TokenTest.java (73%) rename {springboot-starter-security-jwt => springboot-starter-security}/src/test/resources/application.properties (77%) diff --git a/README.md b/README.md index 16844a72..6b653203 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ v.3.x 为springboot 3.x版本,使用jdk17版本 * springboot-starter | Springboot领域驱动框架 * springboot-starter-data-fast | 快速数据呈现框架 -* springboot-starter-security-jwt | security&jwt权限框架 +* springboot-starter-security | security权限框架支持基于JWT的无状态权限认证与Redis的有状态权限认证 ## SpringBoot DDD Architecture | SpringBoot DDD 框架图 @@ -44,7 +44,7 @@ v.3.x 为springboot 3.x版本,使用jdk17版本 com.codingapi.springboot - springboot-starter-security-jwt + springboot-starter-security ${last.version} diff --git a/docs/wiki/springboot-starter-security-jwt.md b/docs/wiki/springboot-starter-security.md similarity index 79% rename from docs/wiki/springboot-starter-security-jwt.md rename to docs/wiki/springboot-starter-security.md index d7c71b5b..d33c5c4a 100644 --- a/docs/wiki/springboot-starter-security-jwt.md +++ b/docs/wiki/springboot-starter-security.md @@ -1,18 +1,28 @@ -springboot-starter-security-jwt 功能介绍 +springboot-starter-security 功能介绍 + +支持无状态的JWT和有状态的redis两种不同的token机制 配置文件,默认参数即说明 ```properties +# JWT开关 +codingapi.security.jwt.enable=true # JWT密钥 需大于32位的字符串 -codingapi.security.jwt-secret=codingapi.security.jwt.secretkey +codingapi.security.jwt.secret-key=codingapi.security.jwt.secretkey + +# JWT 有效时间(毫秒) 15分钟有效期 1000*60*15=900000 +codingapi.security.jwt.valid-time=900000 +# JWT 更换令牌时间(毫秒) 10分钟后更换令牌 1000*60*10=600000 +codingapi.security.jwt.rest-time=600000 + # JWT AES密钥 codingapi.security.ase-key=QUNEWCQlXiYqJCNYQ1phc0FDRFgkJV4mKiQjWENaYXM= # JWT AES IV -codingapi.security.aes-iv=QUNYRkdIQEVEUyNYQ1phcw== +codingapi.security.ase-iv=QUNYRkdIQEVEUyNYQ1phcw== -# JWT 有效时间(毫秒) 15分钟有效期 1000*60*15=900000 -codingapi.security.jwt-time=900000 -# JWT 更换令牌时间(毫秒) 10分钟后更换令牌 1000*60*10=600000 -codingapi.security.jwt-rest-time=600000 +# Redis开关 +#codingapi.security.redis.enable=true +#spring.data.redis.host=localhost +#spring.data.redis.port=6379 # Security 配置 请求权限拦截地址 codingapi.security.authenticated-urls=/api/** @@ -52,7 +62,7 @@ security默认的账户密码为admin/admin,可以通过重写UserDetailsServi ## 登录拦截 可以通过重写SecurityLoginHandler来实现自定义登录拦截,preHandle登录前的拦截处理,postHandle登录后的拦截处理 -```java +``` @Bean public SecurityLoginHandler securityLoginHandler() { return new SecurityLoginHandler() { @@ -74,15 +84,15 @@ security默认的账户密码为admin/admin,可以通过重写UserDetailsServi 通过TokenContext获取当前用户信息 ```java @GetMapping("/user") - public String user(){ - return TokenContext.current().getUsername(); - } +public String user(){ + return TokenContext.current().getUsername(); +} ``` 可以通过Token的extra字段来存储用户的更多信息,然后通过TokenContext获取 ```java @GetMapping("/user") - public String user(){ - return TokenContext.current().getExtra("user"); - } +public String user(){ + return TokenContext.current().getExtra("user"); +} ``` diff --git a/pom.xml b/pom.xml index 17d00ac7..6bcb044d 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.7.10 + 2.8.0 https://github.com/codingapi/springboot-framewrok springboot-parent @@ -32,7 +32,7 @@ 3.1.0 ${project.version} 2.0.42 - 0.12.3 + 0.12.5 2.15.0 1.8.1 1.11.0 @@ -143,7 +143,7 @@ com.codingapi.springboot - springboot-starter-security-jwt + springboot-starter-security ${codingapi.framework.version} @@ -249,7 +249,7 @@ springboot-starter - springboot-starter-security-jwt + springboot-starter-security springboot-starter-data-fast @@ -260,7 +260,7 @@ springboot-starter - springboot-starter-security-jwt + springboot-starter-security springboot-starter-data-fast @@ -309,7 +309,7 @@ springboot-starter - springboot-starter-security-jwt + springboot-starter-security springboot-starter-data-fast diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index dba73ff4..027c6057 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.7.10 + 2.8.0 4.0.0 diff --git a/springboot-starter-security-jwt/src/main/resources/META-INF/spring.factories b/springboot-starter-security-jwt/src/main/resources/META-INF/spring.factories deleted file mode 100644 index b7882c59..00000000 --- a/springboot-starter-security-jwt/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,4 +0,0 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -com.codingapi.springboot.security.configurer.WebSecurityConfigurer,\ -com.codingapi.springboot.security.crypto.MyCryptoConfiguration,\ -com.codingapi.springboot.security.AutoConfiguration \ No newline at end of file diff --git a/springboot-starter-security-jwt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/springboot-starter-security-jwt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 69b9af0d..00000000 --- a/springboot-starter-security-jwt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,3 +0,0 @@ -com.codingapi.springboot.security.configurer.WebSecurityConfigurer -com.codingapi.springboot.security.crypto.MyCryptoConfiguration -com.codingapi.springboot.security.AutoConfiguration \ No newline at end of file diff --git a/springboot-starter-security-jwt/pom.xml b/springboot-starter-security/pom.xml similarity index 73% rename from springboot-starter-security-jwt/pom.xml rename to springboot-starter-security/pom.xml index 73c3f64f..f7f43525 100644 --- a/springboot-starter-security-jwt/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,13 +6,13 @@ springboot-parent com.codingapi.springboot - 2.7.10 + 2.8.0 - springboot-starter-security-jwt + springboot-starter-security - springboot-starter-security-jwt - springboot-starter-security-jwt project for Spring Boot + springboot-starter-security + springboot-starter-security project for Spring Boot 8 @@ -30,19 +30,28 @@ spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-data-redis + provided + + io.jsonwebtoken jjwt-api + provided io.jsonwebtoken jjwt-impl + provided io.jsonwebtoken jjwt-jackson + provided diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java similarity index 86% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java index fb557e55..27c81da9 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java @@ -4,9 +4,9 @@ import com.codingapi.springboot.security.controller.VersionController; import com.codingapi.springboot.security.dto.request.LoginRequest; import com.codingapi.springboot.security.filter.*; -import com.codingapi.springboot.security.jwt.Jwt; -import com.codingapi.springboot.security.jwt.Token; -import com.codingapi.springboot.security.properties.SecurityJwtProperties; +import com.codingapi.springboot.security.gateway.Token; +import com.codingapi.springboot.security.gateway.TokenGateway; +import com.codingapi.springboot.security.properties.CodingApiSecurityProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -86,8 +86,8 @@ public void postHandle(HttpServletRequest request, HttpServletResponse response, @Bean @ConditionalOnMissingBean - public SecurityFilterChain filterChain(HttpSecurity security, Jwt jwt, SecurityLoginHandler loginHandler, - SecurityJwtProperties properties, AuthenticationTokenFilter authenticationTokenFilter) throws Exception { + public SecurityFilterChain filterChain(HttpSecurity security, TokenGateway tokenGateway, SecurityLoginHandler loginHandler, + CodingApiSecurityProperties properties, AuthenticationTokenFilter authenticationTokenFilter) throws Exception { //disable basic auth security.httpBasic().disable(); @@ -96,7 +96,7 @@ public SecurityFilterChain filterChain(HttpSecurity security, Jwt jwt, SecurityL if (properties.isDisableCsrf()) { security.csrf().disable(); } - security.apply(new HttpSecurityConfigurer(jwt, loginHandler, properties, authenticationTokenFilter)); + security.apply(new HttpSecurityConfigurer(tokenGateway, loginHandler, properties, authenticationTokenFilter)); security .exceptionHandling() .authenticationEntryPoint(new MyUnAuthenticationEntryPoint()) @@ -135,19 +135,13 @@ public AuthenticationProvider authenticationProvider(UserDetailsService userDeta } - @Bean - @ConditionalOnMissingBean - public Jwt jwt(SecurityJwtProperties properties) { - return new Jwt(properties.getJwtSecretKey(), properties.getJwtTime(), properties.getJwtRestTime()); - } - @Bean - public WebMvcConfigurer corsConfigurer(SecurityJwtProperties securityJwtProperties) { + public WebMvcConfigurer corsConfigurer(CodingApiSecurityProperties securityProperties) { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { - if (securityJwtProperties.isDisableCors()) { + if (securityProperties.isDisableCors()) { registry.addMapping("/**") .allowedHeaders("*") .allowedMethods("*") @@ -164,8 +158,8 @@ public void addCorsMappings(CorsRegistry registry) { @Bean @ConfigurationProperties(prefix = "codingapi.security") - public SecurityJwtProperties securityJwtProperties() { - return new SecurityJwtProperties(); + public CodingApiSecurityProperties codingApiSecurityProperties() { + return new CodingApiSecurityProperties(); } diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/configurer/HttpSecurityConfigurer.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/configurer/HttpSecurityConfigurer.java similarity index 72% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/configurer/HttpSecurityConfigurer.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/configurer/HttpSecurityConfigurer.java index c95ec34e..e6258f1c 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/configurer/HttpSecurityConfigurer.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/configurer/HttpSecurityConfigurer.java @@ -4,8 +4,8 @@ import com.codingapi.springboot.security.filter.MyAuthenticationFilter; import com.codingapi.springboot.security.filter.MyLoginFilter; import com.codingapi.springboot.security.filter.SecurityLoginHandler; -import com.codingapi.springboot.security.jwt.Jwt; -import com.codingapi.springboot.security.properties.SecurityJwtProperties; +import com.codingapi.springboot.security.gateway.TokenGateway; +import com.codingapi.springboot.security.properties.CodingApiSecurityProperties; import lombok.AllArgsConstructor; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -14,16 +14,16 @@ @AllArgsConstructor public class HttpSecurityConfigurer extends AbstractHttpConfigurer { - private final Jwt jwt; + private final TokenGateway tokenGateway; private final SecurityLoginHandler securityLoginHandler; - private final SecurityJwtProperties securityJwtProperties; + private final CodingApiSecurityProperties securityProperties; private final AuthenticationTokenFilter authenticationTokenFilter; @Override public void configure(HttpSecurity security) throws Exception { AuthenticationManager manager = security.getSharedObject(AuthenticationManager.class); - security.addFilter(new MyLoginFilter(manager, jwt,securityLoginHandler, securityJwtProperties)); - security.addFilter(new MyAuthenticationFilter(manager,securityJwtProperties,jwt,authenticationTokenFilter)); + security.addFilter(new MyLoginFilter(manager, tokenGateway,securityLoginHandler, securityProperties)); + security.addFilter(new MyAuthenticationFilter(manager,securityProperties,tokenGateway,authenticationTokenFilter)); } } diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/configurer/WebSecurityConfigurer.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/configurer/WebSecurityConfigurer.java similarity index 71% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/configurer/WebSecurityConfigurer.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/configurer/WebSecurityConfigurer.java index 5bd1ebd5..06d84d7c 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/configurer/WebSecurityConfigurer.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/configurer/WebSecurityConfigurer.java @@ -1,6 +1,6 @@ package com.codingapi.springboot.security.configurer; -import com.codingapi.springboot.security.properties.SecurityJwtProperties; +import com.codingapi.springboot.security.properties.CodingApiSecurityProperties; import lombok.AllArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.WebSecurity; @@ -10,12 +10,12 @@ @AllArgsConstructor public class WebSecurityConfigurer implements WebSecurityCustomizer { - private final SecurityJwtProperties securityJwtProperties; + private final CodingApiSecurityProperties securityProperties; @Override public void customize(WebSecurity web) { //ignoring security filters request url - web.ignoring().antMatchers(securityJwtProperties.getIgnoreUrls()); + web.ignoring().antMatchers(securityProperties.getIgnoreUrls()); } } diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/controller/VersionController.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/controller/VersionController.java similarity index 100% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/controller/VersionController.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/controller/VersionController.java diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/crypto/MyAES.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/crypto/AESTools.java similarity index 84% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/crypto/MyAES.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/crypto/AESTools.java index b3536ca8..efe4a7a8 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/crypto/MyAES.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/crypto/AESTools.java @@ -6,20 +6,20 @@ import java.nio.charset.StandardCharsets; import java.util.Base64; -public class MyAES { +public class AESTools { - private final static MyAES instance = new MyAES(); + private final static AESTools instance = new AESTools(); private AES aes; - private MyAES() { + private AESTools() { } void init(AES aes) { this.aes = aes; } - public static MyAES getInstance() { + public static AESTools getInstance() { return instance; } diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/crypto/MyCryptoConfiguration.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/crypto/SecurityCryptoConfiguration.java similarity index 69% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/crypto/MyCryptoConfiguration.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/crypto/SecurityCryptoConfiguration.java index 0d4b2bd4..6820dc5c 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/crypto/MyCryptoConfiguration.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/crypto/SecurityCryptoConfiguration.java @@ -1,7 +1,7 @@ package com.codingapi.springboot.security.crypto; import com.codingapi.springboot.framework.crypto.AES; -import com.codingapi.springboot.security.properties.SecurityJwtProperties; +import com.codingapi.springboot.security.properties.CodingApiSecurityProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -9,15 +9,14 @@ import java.util.Base64; @Configuration -public class MyCryptoConfiguration { +public class SecurityCryptoConfiguration { @Bean @ConditionalOnMissingBean - public AES aes(SecurityJwtProperties properties) throws Exception { + public AES aes(CodingApiSecurityProperties properties) throws Exception { AES aes = new AES(Base64.getDecoder().decode(properties.getAseKey().getBytes()), Base64.getDecoder().decode(properties.getAseIv())); - MyAES.getInstance().init(aes); + AESTools.getInstance().init(aes); return aes; } - } diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/dto/request/LoginRequest.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/dto/request/LoginRequest.java similarity index 100% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/dto/request/LoginRequest.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/dto/request/LoginRequest.java diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/dto/request/LoginRequestContext.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/dto/request/LoginRequestContext.java similarity index 100% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/dto/request/LoginRequestContext.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/dto/request/LoginRequestContext.java diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/dto/response/LoginResponse.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/dto/response/LoginResponse.java similarity index 100% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/dto/response/LoginResponse.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/dto/response/LoginResponse.java diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/exception/TokenExpiredException.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/exception/TokenExpiredException.java similarity index 100% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/exception/TokenExpiredException.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/exception/TokenExpiredException.java diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/AuthenticationTokenFilter.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/AuthenticationTokenFilter.java similarity index 100% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/AuthenticationTokenFilter.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/AuthenticationTokenFilter.java diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyAccessDeniedHandler.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAccessDeniedHandler.java similarity index 100% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyAccessDeniedHandler.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAccessDeniedHandler.java diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java similarity index 77% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java index c326bd75..1b7286cf 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java @@ -3,9 +3,9 @@ import com.alibaba.fastjson.JSONObject; import com.codingapi.springboot.framework.dto.response.Response; import com.codingapi.springboot.security.exception.TokenExpiredException; -import com.codingapi.springboot.security.jwt.Jwt; -import com.codingapi.springboot.security.jwt.Token; -import com.codingapi.springboot.security.properties.SecurityJwtProperties; +import com.codingapi.springboot.security.gateway.Token; +import com.codingapi.springboot.security.gateway.TokenGateway; +import com.codingapi.springboot.security.properties.CodingApiSecurityProperties; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.springframework.security.authentication.AuthenticationManager; @@ -26,16 +26,16 @@ public class MyAuthenticationFilter extends BasicAuthenticationFilter { private final static String TOKEN_KEY = "Authorization"; - private final Jwt jwt; + private final TokenGateway tokenGateway; - private final SecurityJwtProperties securityJwtProperties; + private final CodingApiSecurityProperties securityProperties; private final AuthenticationTokenFilter authenticationTokenFilter; private final AntPathMatcher antPathMatcher = new AntPathMatcher(); - public MyAuthenticationFilter(AuthenticationManager manager, SecurityJwtProperties securityJwtProperties, Jwt jwt,AuthenticationTokenFilter authenticationTokenFilter) { + public MyAuthenticationFilter(AuthenticationManager manager, CodingApiSecurityProperties securityProperties, TokenGateway tokenGateway,AuthenticationTokenFilter authenticationTokenFilter) { super(manager); - this.jwt = jwt; - this.securityJwtProperties = securityJwtProperties; + this.tokenGateway = tokenGateway; + this.securityProperties = securityProperties; this.authenticationTokenFilter = authenticationTokenFilter; } @@ -43,7 +43,7 @@ public MyAuthenticationFilter(AuthenticationManager manager, SecurityJwtProperti @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { log.debug("token authentication ~"); - for (String antUrl : securityJwtProperties.getAuthenticatedUrls()) { + for (String antUrl : securityProperties.getAuthenticatedUrls()) { if(antPathMatcher.match(antUrl,request.getRequestURI())) { String sign = request.getHeader(TOKEN_KEY); @@ -52,9 +52,9 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse return; } - Token token = jwt.parser(sign); + Token token = tokenGateway.parser(sign); if (token.canRestToken()) { - Token newSign = jwt.create(token.getUsername(), token.decodeIv(), token.getAuthorities(), token.getExtra()); + Token newSign = tokenGateway.create(token.getUsername(), token.decodeIv(), token.getAuthorities(), token.getExtra()); log.info("reset token "); response.setHeader(TOKEN_KEY, newSign.getToken()); } diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java similarity index 87% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java index 27c1bfb9..2bf4ab52 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java @@ -6,10 +6,10 @@ import com.codingapi.springboot.security.dto.request.LoginRequest; import com.codingapi.springboot.security.dto.request.LoginRequestContext; import com.codingapi.springboot.security.dto.response.LoginResponse; -import com.codingapi.springboot.security.jwt.Jwt; -import com.codingapi.springboot.security.jwt.Token; -import com.codingapi.springboot.security.jwt.TokenContext; -import com.codingapi.springboot.security.properties.SecurityJwtProperties; +import com.codingapi.springboot.security.gateway.Token; +import com.codingapi.springboot.security.gateway.TokenContext; +import com.codingapi.springboot.security.gateway.TokenGateway; +import com.codingapi.springboot.security.properties.CodingApiSecurityProperties; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.springframework.security.authentication.AuthenticationManager; @@ -33,15 +33,15 @@ @Slf4j public class MyLoginFilter extends UsernamePasswordAuthenticationFilter { - private final Jwt jwt; + private final TokenGateway tokenGateway; private final SecurityLoginHandler loginHandler; - public MyLoginFilter(AuthenticationManager authenticationManager, Jwt jwt, SecurityLoginHandler loginHandler, SecurityJwtProperties securityJwtProperties) { + public MyLoginFilter(AuthenticationManager authenticationManager, TokenGateway tokenGateway, SecurityLoginHandler loginHandler, CodingApiSecurityProperties securityProperties) { super(authenticationManager); - this.jwt = jwt; + this.tokenGateway = tokenGateway; this.loginHandler = loginHandler; - this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(securityJwtProperties.getLoginProcessingUrl(), "POST")); + this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(securityProperties.getLoginProcessingUrl(), "POST")); } @Override @@ -72,7 +72,7 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR User user = (User) authResult.getPrincipal(); LoginRequest loginRequest = LoginRequestContext.getInstance().get(); - Token token = jwt.create(user.getUsername(), loginRequest.getPassword(), + Token token = tokenGateway.create(user.getUsername(), loginRequest.getPassword(), user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()), TokenContext.getExtra()); diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLogoutHandler.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLogoutHandler.java similarity index 100% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLogoutHandler.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLogoutHandler.java diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLogoutSuccessHandler.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLogoutSuccessHandler.java similarity index 100% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyLogoutSuccessHandler.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLogoutSuccessHandler.java diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyUnAuthenticationEntryPoint.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyUnAuthenticationEntryPoint.java similarity index 100% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/MyUnAuthenticationEntryPoint.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyUnAuthenticationEntryPoint.java diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java similarity index 89% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java index 8c375480..97de8972 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java @@ -1,7 +1,7 @@ package com.codingapi.springboot.security.filter; import com.codingapi.springboot.security.dto.request.LoginRequest; -import com.codingapi.springboot.security.jwt.Token; +import com.codingapi.springboot.security.gateway.Token; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/jwt/Token.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/gateway/Token.java similarity index 91% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/jwt/Token.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/gateway/Token.java index a3fd6345..9579a9b3 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/jwt/Token.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/gateway/Token.java @@ -1,8 +1,8 @@ -package com.codingapi.springboot.security.jwt; +package com.codingapi.springboot.security.gateway; import com.alibaba.fastjson.JSONObject; import com.codingapi.springboot.framework.serializable.JsonSerializable; -import com.codingapi.springboot.security.crypto.MyAES; +import com.codingapi.springboot.security.crypto.AESTools; import com.codingapi.springboot.security.exception.TokenExpiredException; import lombok.Getter; import lombok.NoArgsConstructor; @@ -34,7 +34,7 @@ public Token(String username, String iv,String extra, List authorities, this.username = username; this.extra = extra; if(iv!=null) { - this.iv = MyAES.getInstance().encode(iv); + this.iv = AESTools.getInstance().encode(iv); } this.authorities = authorities; this.expireTime = System.currentTimeMillis() + expireValue; @@ -56,7 +56,7 @@ public String decodeIv(){ if(iv==null){ return null; } - return MyAES.getInstance().decode(iv); + return AESTools.getInstance().decode(iv); } diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/jwt/TokenContext.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/gateway/TokenContext.java similarity index 91% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/jwt/TokenContext.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/gateway/TokenContext.java index 99b37a7c..e3ecaa8f 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/jwt/TokenContext.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/gateway/TokenContext.java @@ -1,4 +1,4 @@ -package com.codingapi.springboot.security.jwt; +package com.codingapi.springboot.security.gateway; import org.springframework.security.core.context.SecurityContextHolder; diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/gateway/TokenGateway.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/gateway/TokenGateway.java new file mode 100644 index 00000000..b339079d --- /dev/null +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/gateway/TokenGateway.java @@ -0,0 +1,23 @@ +package com.codingapi.springboot.security.gateway; + +import java.util.List; + +public interface TokenGateway { + + Token create(String username, String iv, List authorities, String extra); + + default Token create(String username, String iv, List authorities) { + return create(username, iv, authorities, null); + } + + default Token create(String username, List authorities) { + return create(username, null, authorities, null); + } + + default Token create(String username, List authorities, String extra) { + return create(username, null, authorities, extra); + } + + Token parser(String sign); + +} diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/jwt/JWTSecurityConfiguration.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/jwt/JWTSecurityConfiguration.java new file mode 100644 index 00000000..54df5b3c --- /dev/null +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/jwt/JWTSecurityConfiguration.java @@ -0,0 +1,35 @@ +package com.codingapi.springboot.security.jwt; + +import com.codingapi.springboot.security.gateway.TokenGateway; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConditionalOnProperty(prefix = "codingapi.security.jwt", name = "enable", havingValue = "true") +public class JWTSecurityConfiguration { + + + @Bean + @ConfigurationProperties(prefix = "codingapi.security.jwt") + public SecurityJWTProperties securityJWTProperties() { + return new SecurityJWTProperties(); + } + + + @Bean + @ConditionalOnMissingBean + public JwtTokenGateway jwtTokenGateway(SecurityJWTProperties properties) { + return new JwtTokenGateway(properties); + } + + + @Bean + @ConditionalOnMissingBean + public TokenGateway jwtTokenGatewayImpl(JwtTokenGateway jwtTokenGateway) { + return new JWTTokenGatewayImpl(jwtTokenGateway); + } + +} diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/jwt/JWTTokenGatewayImpl.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/jwt/JWTTokenGatewayImpl.java new file mode 100644 index 00000000..4a890ab7 --- /dev/null +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/jwt/JWTTokenGatewayImpl.java @@ -0,0 +1,25 @@ +package com.codingapi.springboot.security.jwt; + +import com.codingapi.springboot.security.gateway.Token; +import com.codingapi.springboot.security.gateway.TokenGateway; + +import java.util.List; + +public class JWTTokenGatewayImpl implements TokenGateway { + + private final JwtTokenGateway jwtTokenGateway; + + public JWTTokenGatewayImpl(JwtTokenGateway jwtTokenGateway) { + this.jwtTokenGateway = jwtTokenGateway; + } + + @Override + public Token create(String username, String password, List authorities, String extra) { + return jwtTokenGateway.create(username, password, authorities, extra); + } + + @Override + public Token parser(String sign) { + return jwtTokenGateway.parser(sign); + } +} diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/jwt/Jwt.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/jwt/JwtTokenGateway.java similarity index 78% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/jwt/Jwt.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/jwt/JwtTokenGateway.java index 75f93713..57dab485 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/jwt/Jwt.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/jwt/JwtTokenGateway.java @@ -2,6 +2,7 @@ import com.alibaba.fastjson.JSONObject; import com.codingapi.springboot.framework.exception.LocaleMessageException; +import com.codingapi.springboot.security.gateway.Token; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jwts; @@ -11,19 +12,19 @@ import java.nio.charset.StandardCharsets; import java.util.List; -public class Jwt { +public class JwtTokenGateway { private final SecretKey key; - private final int jwtTime; - private final int jwtRestTime; + private final int validTime; + private final int restTime; - public Jwt(String secretKey, int jwtTime, int jwtRestTime) { - this.key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8)); - this.jwtTime = jwtTime; - this.jwtRestTime = jwtRestTime; + public JwtTokenGateway(SecurityJWTProperties properties) { + this.key = Keys.hmacShaKeyFor(properties.getSecretKey().getBytes(StandardCharsets.UTF_8)); + this.validTime = properties.getValidTime(); + this.restTime = properties.getRestTime(); } - public Token create(String username, List authorities,String extra){ + public Token create(String username, List authorities, String extra){ return create(username, null,authorities, extra); } @@ -36,7 +37,7 @@ public Token create(String username, String iv, List authorities){ } public Token create(String username, String iv,List authorities,String extra){ - Token token = new Token(username, iv,extra, authorities, jwtTime, jwtRestTime); + Token token = new Token(username, iv,extra, authorities, validTime, restTime); String jwt = Jwts.builder().subject(token.toJson()).signWith(key).compact(); token.setToken(jwt); return token; @@ -54,4 +55,4 @@ public Token parser(String sign) { throw new LocaleMessageException("token.error", exp.getMessage()); } } -} \ No newline at end of file +} diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/jwt/SecurityJWTProperties.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/jwt/SecurityJWTProperties.java new file mode 100644 index 00000000..d4ca0537 --- /dev/null +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/jwt/SecurityJWTProperties.java @@ -0,0 +1,35 @@ +package com.codingapi.springboot.security.jwt; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class SecurityJWTProperties { + + + /** + * 是否启用JWT + */ + private boolean enable = true; + + /** + * JWT密钥 + * 需大于32位的字符串 + */ + private String secretKey = "codingapi.security.jwt.secretkey"; + + + /** + * JWT 有效时间(毫秒) + * 15分钟有效期 1000*60*15=900000 + */ + private int validTime = 900000; + + /** + * JWT 更换令牌时间(毫秒) + * 10分钟后更换令牌 1000*60*10=600000 + */ + private int restTime = 600000; + +} diff --git a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/properties/SecurityJwtProperties.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/properties/CodingApiSecurityProperties.java similarity index 69% rename from springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/properties/SecurityJwtProperties.java rename to springboot-starter-security/src/main/java/com/codingapi/springboot/security/properties/CodingApiSecurityProperties.java index 5c296e80..4a33d91b 100644 --- a/springboot-starter-security-jwt/src/main/java/com/codingapi/springboot/security/properties/SecurityJwtProperties.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/properties/CodingApiSecurityProperties.java @@ -5,37 +5,7 @@ @Setter @Getter -public class SecurityJwtProperties { - - /** - * JWT密钥 - * 需大于32位的字符串 - */ - private String jwtSecretKey = "codingapi.security.jwt.secretkey"; - - - /** - * aes key - */ - private String aseKey = "QUNEWCQlXiYqJCNYQ1phc0FDRFgkJV4mKiQjWENaYXM="; - - /** - * aes iv - */ - private String aseIv = "QUNYRkdIQEVEUyNYQ1phcw=="; - - - /** - * JWT 有效时间(毫秒) - * 15分钟有效期 1000*60*15=900000 - */ - private int jwtTime = 900000; - - /** - * JWT 更换令牌时间(毫秒) - * 10分钟后更换令牌 1000*60*10=600000 - */ - private int jwtRestTime = 600000; +public class CodingApiSecurityProperties { /** * 权限拦截URL @@ -58,6 +28,17 @@ public class SecurityJwtProperties { */ private String ignoreUrls = "/open/**"; + /** + * aes key + */ + private String aseKey = "QUNEWCQlXiYqJCNYQ1phc0FDRFgkJV4mKiQjWENaYXM="; + + /** + * aes iv + */ + private String aseIv = "QUNYRkdIQEVEUyNYQ1phcw=="; + + /** * 启用禁用CSRF */ diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisSecurityConfiguration.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisSecurityConfiguration.java new file mode 100644 index 00000000..c4bf141b --- /dev/null +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisSecurityConfiguration.java @@ -0,0 +1,35 @@ +package com.codingapi.springboot.security.redis; + +import com.codingapi.springboot.security.gateway.TokenGateway; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.RedisTemplate; + +@Configuration +@ConditionalOnProperty(prefix = "codingapi.security.redis", name = "enable", havingValue = "true") +public class RedisSecurityConfiguration { + + + @Bean + @ConfigurationProperties(prefix = "codingapi.security.redis") + public SecurityRedisProperties securityRedisProperties() { + return new SecurityRedisProperties(); + } + + + @Bean + @ConditionalOnMissingBean + public RedisTokenGateway redisTokenGateway(RedisTemplate redisTemplate, SecurityRedisProperties properties) { + return new RedisTokenGateway(redisTemplate, properties); + } + + @Bean + @ConditionalOnMissingBean + public TokenGateway redisTokenGatewayImpl(RedisTokenGateway redisTokenGateway) { + return new RedisTokenGatewayImpl(redisTokenGateway); + } + +} diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGateway.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGateway.java new file mode 100644 index 00000000..d4eb4257 --- /dev/null +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGateway.java @@ -0,0 +1,65 @@ +package com.codingapi.springboot.security.redis; + +import com.alibaba.fastjson2.JSONObject; +import com.codingapi.springboot.security.gateway.Token; +import org.springframework.data.redis.core.RedisTemplate; + +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; + +public class RedisTokenGateway { + + private final RedisTemplate redisTemplate; + private final int validTime; + private final int restTime; + + public RedisTokenGateway(RedisTemplate redisTemplate, SecurityRedisProperties properties) { + this.redisTemplate = redisTemplate; + this.validTime = properties.getValidTime(); + this.restTime = properties.getRestTime(); + } + + public Token create(String username, String iv, List authorities, String extra) { + Token token = new Token(username, iv, extra, authorities, validTime, restTime); + String key = String.format("%s:%s", username, UUID.randomUUID().toString().replaceAll("-", "")); + token.setToken(key); + redisTemplate.opsForValue().set(key, token.toJson(), validTime, TimeUnit.MILLISECONDS); + return token; + } + + public Token parser(String sign) { + String json = redisTemplate.opsForValue().get(sign); + if (json == null) { + return null; + } + return JSONObject.parseObject(json, Token.class); + } + + public void removeToken(String token) { + redisTemplate.delete(token); + } + + public void removeUsername(String username) { + Set keys = redisTemplate.keys(username + ":*"); + if (keys != null && !keys.isEmpty()) { + redisTemplate.delete(keys); + } + } + + public void removeUsername(String username, Predicate predicate) { + Set keys = redisTemplate.keys(username + ":*"); + if (keys != null && !keys.isEmpty()) { + for (String key : keys) { + Token token = parser(key); + if (token != null && predicate.test(token)) { + redisTemplate.delete(key); + } + } + } + } + + +} diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGatewayImpl.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGatewayImpl.java new file mode 100644 index 00000000..19453d66 --- /dev/null +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGatewayImpl.java @@ -0,0 +1,26 @@ +package com.codingapi.springboot.security.redis; + +import com.codingapi.springboot.security.gateway.Token; +import com.codingapi.springboot.security.gateway.TokenGateway; + +import java.util.List; + +public class RedisTokenGatewayImpl implements TokenGateway { + + private final RedisTokenGateway redisTokenGateway; + + public RedisTokenGatewayImpl(RedisTokenGateway redisTokenGateway) { + this.redisTokenGateway = redisTokenGateway; + } + + @Override + public Token create(String username, String iv, List authorities, String extra) { + return redisTokenGateway.create(username, iv, authorities, extra); + } + + @Override + public Token parser(String sign) { + return redisTokenGateway.parser(sign); + } + +} diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/SecurityRedisProperties.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/SecurityRedisProperties.java new file mode 100644 index 00000000..14eae9ac --- /dev/null +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/SecurityRedisProperties.java @@ -0,0 +1,27 @@ +package com.codingapi.springboot.security.redis; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class SecurityRedisProperties { + + + /** + * 是否启用redis + */ + private boolean enable = true; + + /** + * 15分钟有效期 1000*60*15=900000 + */ + private int validTime = 900000; + + /** + * 10分钟后更换令牌 1000*60*10=600000 + */ + private int restTime = 600000; + + +} diff --git a/springboot-starter-security/src/main/resources/META-INF/spring.factories b/springboot-starter-security/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..6fbb976a --- /dev/null +++ b/springboot-starter-security/src/main/resources/META-INF/spring.factories @@ -0,0 +1,6 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.codingapi.springboot.security.configurer.WebSecurityConfigurer,\ +com.codingapi.springboot.security.crypto.SecurityCryptoConfiguration,\ +com.codingapi.springboot.security.jwt.JWTSecurityConfiguration,\ +com.codingapi.springboot.security.redis.RedisSecurityConfiguration,\ +com.codingapi.springboot.security.AutoConfiguration \ No newline at end of file diff --git a/springboot-starter-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/springboot-starter-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..59324754 --- /dev/null +++ b/springboot-starter-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,5 @@ +com.codingapi.springboot.security.configurer.WebSecurityConfigurer +com.codingapi.springboot.security.crypto.SecurityCryptoConfiguration +com.codingapi.springboot.security.jwt.JWTSecurityConfiguration +com.codingapi.springboot.security.redis.RedisSecurityConfiguration +com.codingapi.springboot.security.AutoConfiguration \ No newline at end of file diff --git a/springboot-starter-security-jwt/src/test/java/com/codingapi/springboot/security/SecurityJwtApplication.java b/springboot-starter-security/src/test/java/com/codingapi/springboot/security/SecurityJwtApplication.java similarity index 100% rename from springboot-starter-security-jwt/src/test/java/com/codingapi/springboot/security/SecurityJwtApplication.java rename to springboot-starter-security/src/test/java/com/codingapi/springboot/security/SecurityJwtApplication.java diff --git a/springboot-starter-security-jwt/src/test/java/com/codingapi/springboot/security/SecurityJwtApplicationTest.java b/springboot-starter-security/src/test/java/com/codingapi/springboot/security/SecurityJwtApplicationTest.java similarity index 100% rename from springboot-starter-security-jwt/src/test/java/com/codingapi/springboot/security/SecurityJwtApplicationTest.java rename to springboot-starter-security/src/test/java/com/codingapi/springboot/security/SecurityJwtApplicationTest.java diff --git a/springboot-starter-security-jwt/src/test/java/com/codingapi/springboot/security/controller/DemoController.java b/springboot-starter-security/src/test/java/com/codingapi/springboot/security/controller/DemoController.java similarity index 100% rename from springboot-starter-security-jwt/src/test/java/com/codingapi/springboot/security/controller/DemoController.java rename to springboot-starter-security/src/test/java/com/codingapi/springboot/security/controller/DemoController.java diff --git a/springboot-starter-security-jwt/src/test/java/com/codingapi/springboot/security/jwt/TestVO.java b/springboot-starter-security/src/test/java/com/codingapi/springboot/security/jwt/TestVO.java similarity index 100% rename from springboot-starter-security-jwt/src/test/java/com/codingapi/springboot/security/jwt/TestVO.java rename to springboot-starter-security/src/test/java/com/codingapi/springboot/security/jwt/TestVO.java diff --git a/springboot-starter-security-jwt/src/test/java/com/codingapi/springboot/security/jwt/TokenTest.java b/springboot-starter-security/src/test/java/com/codingapi/springboot/security/jwt/TokenTest.java similarity index 73% rename from springboot-starter-security-jwt/src/test/java/com/codingapi/springboot/security/jwt/TokenTest.java rename to springboot-starter-security/src/test/java/com/codingapi/springboot/security/jwt/TokenTest.java index 3010f7fb..b5611674 100644 --- a/springboot-starter-security-jwt/src/test/java/com/codingapi/springboot/security/jwt/TokenTest.java +++ b/springboot-starter-security/src/test/java/com/codingapi/springboot/security/jwt/TokenTest.java @@ -1,6 +1,8 @@ package com.codingapi.springboot.security.jwt; import com.codingapi.springboot.security.exception.TokenExpiredException; +import com.codingapi.springboot.security.gateway.Token; +import com.codingapi.springboot.security.gateway.TokenGateway; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -14,7 +16,7 @@ class TokenTest { @Autowired - private Jwt jwt; + private TokenGateway tokenGateway; @Test void verify1() throws TokenExpiredException { @@ -22,10 +24,10 @@ void verify1() throws TokenExpiredException { String iv = "123456"; List authorities = Collections.singletonList("ADMIN"); - Token token =jwt.create(username,iv,authorities); + Token token =tokenGateway.create(username,iv,authorities); token.verify(); - Token data = jwt.parser(token.getToken()); + Token data = tokenGateway.parser(token.getToken()); assertEquals(data.decodeIv(),iv); assertEquals(data.getAuthorities(),authorities); } @@ -35,10 +37,10 @@ void verify2() throws TokenExpiredException { String username = "admin"; List authorities = Collections.singletonList("ADMIN"); - Token token =jwt.create(username,authorities); + Token token =tokenGateway.create(username,authorities); token.verify(); - Token data = jwt.parser(token.getToken()); + Token data = tokenGateway.parser(token.getToken()); assertEquals(data.getUsername(),username); assertEquals(data.getAuthorities(),authorities); } @@ -52,10 +54,10 @@ void verify3() throws TokenExpiredException { String extra = testVO.toJson(); List authorities = Collections.singletonList("ADMIN"); - Token token =jwt.create(username,authorities,extra); + Token token =tokenGateway.create(username,authorities,extra); token.verify(); - Token data = jwt.parser(token.getToken()); + Token data = tokenGateway.parser(token.getToken()); assertEquals(data.parseExtra(TestVO.class).getName(), testVO.getName()); assertEquals(data.getAuthorities(),authorities); } diff --git a/springboot-starter-security-jwt/src/test/resources/application.properties b/springboot-starter-security/src/test/resources/application.properties similarity index 77% rename from springboot-starter-security-jwt/src/test/resources/application.properties rename to springboot-starter-security/src/test/resources/application.properties index 3848c355..d228eabb 100644 --- a/springboot-starter-security-jwt/src/test/resources/application.properties +++ b/springboot-starter-security/src/test/resources/application.properties @@ -1,14 +1,16 @@ server.port=8088 -codingapi.security.jwt-time=10000 -codingapi.security.jwt-rest-time=5000 +codingapi.security.jwt.valid-time=10000 +codingapi.security.jwt.rest-time=5000 + +codingapi.security.jwt.enable=true # JWT密钥 需大于32位的字符串 -codingapi.security.jwt-secret=codingapi.security.jwt.secretkey +codingapi.security.jwt.secret-key=codingapi.security.jwt.secretkey # JWT AES密钥 codingapi.security.ase-key=QUNEWCQlXiYqJCNYQ1phc0FDRFgkJV4mKiQjWENaYXM= # JWT AES IV -codingapi.security.aes-iv=QUNYRkdIQEVEUyNYQ1phcw== +codingapi.security.ase-iv=QUNYRkdIQEVEUyNYQ1phcw== # JWT 有效时间(毫秒) 15分钟有效期 1000*60*15=900000 #codingapi.security.jwt-time=900000 diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 1cf03f54..386380dd 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.7.10 + 2.8.0 springboot-starter From d3c988d86af48b930dfce3d9ace6655df9abe15b Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Wed, 24 Apr 2024 11:00:29 +0800 Subject: [PATCH 041/101] #44 --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../framework/dto/request/SearchRequest.java | 81 +++++++++++++++---- 5 files changed, 70 insertions(+), 19 deletions(-) diff --git a/pom.xml b/pom.xml index 6bcb044d..631d99da 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.8.0 + 2.8.1 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 027c6057..d2ff620f 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.8.0 + 2.8.1 4.0.0 diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index f7f43525..b850a475 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.8.0 + 2.8.1 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 386380dd..cc8b7b28 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.8.0 + 2.8.1 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java index 07c53bdd..2e95c80f 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java @@ -3,6 +3,8 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; +import lombok.Getter; +import lombok.Setter; import org.springframework.data.domain.Sort; import org.springframework.util.StringUtils; import org.springframework.web.context.request.RequestContextHolder; @@ -12,9 +14,7 @@ import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Base64; -import java.util.Enumeration; import java.util.List; -import java.util.stream.Collectors; /** * HttpServletRequest 请求参数解析成 PageRequest对象 @@ -46,17 +46,21 @@ public void setPageSize(int pageSize) { this.removeKeys.add("pageSize"); } - private String decode(String value) { - return new String(Base64.getDecoder().decode(value)); - } - - public void addSort(Sort sort) { pageRequest.addSort(sort); } public void removeFilter(String key) { pageRequest.removeFilter(key); + this.removeKeys.add(key); + } + + public String getParameter(String key) { + return request.getParameter(key); + } + + public String[] getParameterValues(String key) { + return request.getParameterValues(key); } public PageRequest addFilter(String key, Relation relation, Object... value) { @@ -75,6 +79,12 @@ public PageRequest orFilters(Filter... filters) { return pageRequest.orFilters(filters); } + + private String decode(String value) { + return new String(Base64.getDecoder().decode(value)); + } + + static class ClassContent { private final Class clazz; @@ -85,6 +95,12 @@ public ClassContent(Class clazz, PageRequest pageRequest) { this.pageRequest = pageRequest; } + public void addFilter(String key, Relation relation, String value) { + Class keyClass = getKeyType(key); + Object v = parseObject(value, keyClass); + pageRequest.addFilter(key, relation, v); + } + public void addFilter(String key, String value) { Class keyClass = getKeyType(key); Object v = parseObject(value, keyClass); @@ -92,7 +108,7 @@ public void addFilter(String key, String value) { } private Object parseObject(String value, Class keyClass) { - if(value.getClass().equals(keyClass)) { + if (value.getClass().equals(keyClass)) { return value; } return JSON.parseObject(value, keyClass); @@ -124,12 +140,37 @@ private Class getKeyType(String key) { } + @Setter + @Getter + static class ParamOperation { + private String key; + private String type; + + public Relation getOperation() { + return Relation.valueOf(type); + } + } + + private List loadParamOperations() { + String params = request.getParameter("params"); + if (StringUtils.hasLength(params)) { + params = decode(params); + if (JSON.isValid(params)) { + removeKeys.add("params"); + return JSON.parseArray(params, ParamOperation.class); + } + } + return null; + } + public PageRequest toPageRequest(Class clazz) { pageRequest.setCurrent(current); pageRequest.setPageSize(pageSize); ClassContent content = new ClassContent(clazz, pageRequest); + List loadParams = loadParamOperations(); + String sort = request.getParameter("sort"); if (StringUtils.hasLength(sort)) { sort = decode(sort); @@ -157,24 +198,34 @@ public PageRequest toPageRequest(Class clazz) { for (String key : jsonObject.keySet()) { JSONArray value = jsonObject.getJSONArray(key); if (value != null && !value.isEmpty()) { - List values = value.stream().map(Object::toString).collect(Collectors.toList()); + List values = value.stream().map(Object::toString).toList(); content.addFilter(key, values); } } } } - Enumeration enumeration = request.getParameterNames(); - while (enumeration.hasMoreElements()) { - String key = enumeration.nextElement(); + + request.getParameterNames().asIterator().forEachRemaining(key -> { if (!removeKeys.contains(key)) { String value = request.getParameter(key); if (StringUtils.hasLength(value)) { - content.addFilter(key, value); + if (loadParams != null) { + ParamOperation operation = loadParams.stream() + .filter(paramOperation -> paramOperation.getKey().equals(key)) + .findFirst() + .orElse(null); + if (operation != null) { + content.addFilter(key, operation.getOperation(), value); + } else { + content.addFilter(key, value); + } + } else { + content.addFilter(key, value); + } } } - } - + }); return pageRequest; } From e07f982793e4a0b2798255f5117de17a3234ac38 Mon Sep 17 00:00:00 2001 From: xlorne <1991wangliang@gmail.com> Date: Thu, 6 Jun 2024 11:37:18 +0800 Subject: [PATCH 042/101] add SearchRequest getParameterNames --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../framework/dto/request/SearchRequest.java | 13 +++++++++++++ 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 631d99da..8d05b1a1 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.8.1 + 2.8.2 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index d2ff620f..23fd9270 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.8.1 + 2.8.2 4.0.0 diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index b850a475..4912e175 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.8.1 + 2.8.2 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index cc8b7b28..911d6cc0 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.8.1 + 2.8.2 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java index 2e95c80f..c6bc6f60 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java @@ -14,6 +14,7 @@ import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Base64; +import java.util.Enumeration; import java.util.List; /** @@ -21,7 +22,9 @@ */ public class SearchRequest { + @Getter private int current; + @Getter private int pageSize; private final HttpServletRequest request; @@ -63,6 +66,16 @@ public String[] getParameterValues(String key) { return request.getParameterValues(key); } + + public List getParameterNames() { + Enumeration enumeration = request.getParameterNames(); + List list = new ArrayList<>(); + while (enumeration.hasMoreElements()) { + list.add(enumeration.nextElement()); + } + return list; + } + public PageRequest addFilter(String key, Relation relation, Object... value) { return pageRequest.addFilter(key, relation, value); } From 9c726e3c8aa7ac2a013606b3ced65f15ea6d5fa7 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Mon, 17 Jun 2024 21:19:19 +0800 Subject: [PATCH 043/101] update 2.8.3 --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 4 ++-- .../springboot/fast/DataFastConfiguration.java | 13 +++++++------ ...ingRegister.java => FastMvcMappingRegister.java} | 2 +- ...Register.java => FastScriptMappingRegister.java} | 6 +++--- springboot-starter-security/pom.xml | 2 +- .../springboot/security/AutoConfiguration.java | 11 ++++++++--- .../security/dto/response/LoginResponse.java | 1 + .../security/filter/AuthenticationTokenFilter.java | 5 +++-- .../security/filter/MyAuthenticationFilter.java | 2 +- .../springboot/security/filter/MyLoginFilter.java | 9 ++------- .../security/filter/SecurityLoginHandler.java | 5 +++-- springboot-starter/pom.xml | 2 +- 13 files changed, 34 insertions(+), 30 deletions(-) rename springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/mapping/{MvcMappingRegister.java => FastMvcMappingRegister.java} (98%) rename springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/{ScriptMappingRegister.java => FastScriptMappingRegister.java} (85%) diff --git a/pom.xml b/pom.xml index 8d05b1a1..4f2e3de7 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.8.2 + 2.8.3 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 23fd9270..b366a0b8 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.8.2 + 2.8.3 4.0.0 @@ -67,4 +67,4 @@ - \ No newline at end of file + diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/DataFastConfiguration.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/DataFastConfiguration.java index 2fb91bcd..33136595 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/DataFastConfiguration.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/DataFastConfiguration.java @@ -1,8 +1,9 @@ package com.codingapi.springboot.fast; import com.codingapi.springboot.fast.manager.EntityManagerInitializer; -import com.codingapi.springboot.fast.mapping.MvcMappingRegister; -import com.codingapi.springboot.fast.script.ScriptMappingRegister; +import com.codingapi.springboot.fast.mapping.FastMvcMappingRegister; +import com.codingapi.springboot.fast.script.FastScriptMappingRegister; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @@ -19,8 +20,8 @@ public class DataFastConfiguration { @Bean @ConditionalOnMissingBean - public MvcMappingRegister mvcMappingRegister(RequestMappingHandlerMapping handlerMapping) { - return new MvcMappingRegister(handlerMapping); + public FastMvcMappingRegister mvcMappingRegister(@Qualifier("requestMappingHandlerMapping") RequestMappingHandlerMapping handlerMapping) { + return new FastMvcMappingRegister(handlerMapping); } @@ -32,8 +33,8 @@ public EntityManagerInitializer entityManagerInitializer(EntityManager entityMan @Bean - public ScriptMappingRegister scriptMappingRegister(MvcMappingRegister mvcMappingRegister) { - return new ScriptMappingRegister(mvcMappingRegister); + public FastScriptMappingRegister scriptMappingRegister(FastMvcMappingRegister fastMvcMappingRegister) { + return new FastScriptMappingRegister(fastMvcMappingRegister); } } diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/mapping/MvcMappingRegister.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/mapping/FastMvcMappingRegister.java similarity index 98% rename from springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/mapping/MvcMappingRegister.java rename to springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/mapping/FastMvcMappingRegister.java index dfe3ed39..35d95f1e 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/mapping/MvcMappingRegister.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/mapping/FastMvcMappingRegister.java @@ -10,7 +10,7 @@ import java.lang.reflect.Method; @AllArgsConstructor -public class MvcMappingRegister { +public class FastMvcMappingRegister { private final RequestMappingHandlerMapping handlerMapping; diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptMappingRegister.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/FastScriptMappingRegister.java similarity index 85% rename from springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptMappingRegister.java rename to springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/FastScriptMappingRegister.java index a94e9134..9dc8583f 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/ScriptMappingRegister.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/script/FastScriptMappingRegister.java @@ -1,13 +1,13 @@ package com.codingapi.springboot.fast.script; -import com.codingapi.springboot.fast.mapping.MvcMappingRegister; +import com.codingapi.springboot.fast.mapping.FastMvcMappingRegister; import com.codingapi.springboot.framework.dto.response.Response; import lombok.AllArgsConstructor; @AllArgsConstructor -public class ScriptMappingRegister { +public class FastScriptMappingRegister { - private final MvcMappingRegister mappingRegister; + private final FastMvcMappingRegister mappingRegister; /** * test dynamic mapping diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 4912e175..2e34431d 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.8.2 + 2.8.3 springboot-starter-security diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java index 27c81da9..339ae029 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java @@ -3,6 +3,7 @@ import com.codingapi.springboot.security.configurer.HttpSecurityConfigurer; import com.codingapi.springboot.security.controller.VersionController; import com.codingapi.springboot.security.dto.request.LoginRequest; +import com.codingapi.springboot.security.dto.response.LoginResponse; import com.codingapi.springboot.security.filter.*; import com.codingapi.springboot.security.gateway.Token; import com.codingapi.springboot.security.gateway.TokenGateway; @@ -61,7 +62,7 @@ public PasswordEncoder passwordEncoder() { @Bean @ConditionalOnMissingBean public AuthenticationTokenFilter authenticationTokenFilter() { - return (request, response, chain) -> { + return (request, response) -> { }; } @@ -77,8 +78,12 @@ public void preHandle(HttpServletRequest request, HttpServletResponse response, } @Override - public void postHandle(HttpServletRequest request, HttpServletResponse response, LoginRequest handler, Token token) { - + public LoginResponse postHandle(HttpServletRequest request, HttpServletResponse response, LoginRequest loginRequest, Token token) { + LoginResponse loginResponse = new LoginResponse(); + loginResponse.setUsername(token.getUsername()); + loginResponse.setToken(token.getToken()); + loginResponse.setAuthorities(token.getAuthorities()); + return loginResponse; } }; } diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/dto/response/LoginResponse.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/dto/response/LoginResponse.java index cdfc3a3f..d717d36e 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/dto/response/LoginResponse.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/dto/response/LoginResponse.java @@ -12,5 +12,6 @@ public class LoginResponse { private String username; private String token; private List authorities; + private Object data; } diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/AuthenticationTokenFilter.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/AuthenticationTokenFilter.java index 4e094e71..020ea664 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/AuthenticationTokenFilter.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/AuthenticationTokenFilter.java @@ -1,12 +1,13 @@ package com.codingapi.springboot.security.filter; -import javax.servlet.FilterChain; +import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.IOException; public interface AuthenticationTokenFilter { - void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain); + void doFilter(HttpServletRequest request, HttpServletResponse response)throws IOException, ServletException; } diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java index 1b7286cf..ed73501b 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java @@ -66,7 +66,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse } SecurityContextHolder.getContext().setAuthentication(token.getAuthenticationToken()); - authenticationTokenFilter.doFilter(request, response, chain); + authenticationTokenFilter.doFilter(request, response); } } chain.doFilter(request, response); diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java index 2bf4ab52..4f2939be 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java @@ -76,16 +76,11 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()), TokenContext.getExtra()); - LoginResponse login = new LoginResponse(); - login.setUsername(user.getUsername()); - login.setToken(token.getToken()); - login.setAuthorities(token.getAuthorities()); + LoginResponse loginResponse = loginHandler.postHandle(request, response, loginRequest, token); - String content = JSONObject.toJSONString(SingleResponse.of(login)); + String content = JSONObject.toJSONString(SingleResponse.of(loginResponse)); IOUtils.write(content, response.getOutputStream(), StandardCharsets.UTF_8); - loginHandler.postHandle(request, response, loginRequest, token); - LoginRequestContext.getInstance().clean(); } diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java index 97de8972..ed0b5876 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java @@ -1,6 +1,7 @@ package com.codingapi.springboot.security.filter; import com.codingapi.springboot.security.dto.request.LoginRequest; +import com.codingapi.springboot.security.dto.response.LoginResponse; import com.codingapi.springboot.security.gateway.Token; import javax.servlet.http.HttpServletRequest; @@ -9,7 +10,7 @@ public interface SecurityLoginHandler { - void preHandle(HttpServletRequest request, HttpServletResponse response, LoginRequest handler) throws Exception; + void preHandle(HttpServletRequest request, HttpServletResponse response, LoginRequest loginRequest) throws Exception; - void postHandle(HttpServletRequest request, HttpServletResponse response, LoginRequest handler, Token token); + LoginResponse postHandle(HttpServletRequest request, HttpServletResponse response, LoginRequest loginRequest, Token token); } diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 911d6cc0..e168fb42 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.8.2 + 2.8.3 springboot-starter From a0db5272bfa6e4b8a15b4ad0ccae0bafa5fc442d Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Fri, 12 Jul 2024 10:52:47 +0800 Subject: [PATCH 044/101] #48 --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../framework/dto/request/Filter.java | 2 +- .../framework/dto/request/SearchRequest.java | 10 ++++++---- .../framework/rest/RestClientTest.java | 18 +++++++++++++----- 7 files changed, 24 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 4f2e3de7..64f738cd 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.8.3 + 2.8.4 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index b366a0b8..a2a0edf9 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.8.3 + 2.8.4 4.0.0 diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 2e34431d..18424120 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.8.3 + 2.8.4 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index e168fb42..4b79616a 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.8.3 + 2.8.4 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java index 55c901b8..db6c5477 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java @@ -25,7 +25,7 @@ public Filter(String key, Object... value) { } public Filter(String key, Filter... value) { - this(key, null, value); + this(key, null, value); } public static Filter as(String key, Relation relation, Object... value) { diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java index c6bc6f60..b8fc5ae9 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java @@ -16,6 +16,7 @@ import java.util.Base64; import java.util.Enumeration; import java.util.List; +import java.util.stream.Collectors; /** * HttpServletRequest 请求参数解析成 PageRequest对象 @@ -211,15 +212,17 @@ public PageRequest toPageRequest(Class clazz) { for (String key : jsonObject.keySet()) { JSONArray value = jsonObject.getJSONArray(key); if (value != null && !value.isEmpty()) { - List values = value.stream().map(Object::toString).toList(); + List values = value.stream().map(Object::toString).collect(Collectors.toList()); content.addFilter(key, values); } } } } + Enumeration enumeration = request.getParameterNames(); - request.getParameterNames().asIterator().forEachRemaining(key -> { + while (enumeration.hasMoreElements()){ + String key = enumeration.nextElement(); if (!removeKeys.contains(key)) { String value = request.getParameter(key); if (StringUtils.hasLength(value)) { @@ -238,8 +241,7 @@ public PageRequest toPageRequest(Class clazz) { } } } - }); - + } return pageRequest; } diff --git a/springboot-starter/src/test/java/com/codingapi/springboot/framework/rest/RestClientTest.java b/springboot-starter/src/test/java/com/codingapi/springboot/framework/rest/RestClientTest.java index c6a48d79..94a3d3ff 100644 --- a/springboot-starter/src/test/java/com/codingapi/springboot/framework/rest/RestClientTest.java +++ b/springboot-starter/src/test/java/com/codingapi/springboot/framework/rest/RestClientTest.java @@ -5,6 +5,7 @@ import com.codingapi.springboot.framework.rest.properties.HttpProxyProperties; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; +import org.springframework.http.HttpHeaders; import java.net.Proxy; @@ -21,14 +22,21 @@ void okxTest() { proxyProperties.setProxyType(Proxy.Type.HTTP); proxyProperties.setProxyHost("127.0.0.1"); proxyProperties.setProxyPort(7890); + + HttpHeaders headers = new HttpHeaders(); + headers.set("x-simulated-trading","1"); + headers.set("User-Agent", "Application"); RestClient restClient = new RestClient(proxyProperties,baseUrl,5,"{}",null,null); - String response = restClient.get("api/v5/market/candles", RestParam.create() - .add("instId","BTC-USDT") - .add("bar","1m") - .add("limit","300") + String response = restClient.get( + "api/v5/market/candles", + headers, + RestParam.create() + .add("instId","BTC-USDT") + .add("bar","1m") + .add("limit","300") ); log.info("response:{}",response); JSONObject jsonObject = JSONObject.parseObject(response); assertEquals(jsonObject.getJSONArray("data").size(),300); } -} \ No newline at end of file +} From 04b76dc29e65695f2df8acb2fc938ccd8de10115 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Wed, 7 Aug 2024 21:38:39 +0800 Subject: [PATCH 045/101] #49 --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- .../springboot/fast/jdbc/JdbcQuery.java | 11 +++- .../fast/jpa/repository/BaseRepository.java | 16 ++++++ .../repository/DynamicNativeRepository.java | 50 +++++++++++++++++++ .../jpa/repository/DynamicRepository.java | 9 +--- .../fast/jpa/repository/FastRepository.java | 18 ++----- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- 9 files changed, 86 insertions(+), 26 deletions(-) create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/BaseRepository.java create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicNativeRepository.java diff --git a/pom.xml b/pom.xml index 64f738cd..acaafde1 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.8.4 + 2.8.5 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index a2a0edf9..c810d8e4 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.8.4 + 2.8.5 4.0.0 diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQuery.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQuery.java index 423ba1ea..d20e379c 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQuery.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQuery.java @@ -51,13 +51,22 @@ public Page queryForPage(String sql, String countSql, Class clazz, Pag return new PageImpl<>(list, pageRequest, count); } + public Page queryForPage(String sql, Class clazz, PageRequest pageRequest, Object... params) { + String countSql = "select count(1) "+sql; + return this.queryForPage(sql, countSql, clazz, pageRequest, params); + } + public Page> queryForPage(String sql, String countSql, PageRequest pageRequest, Object... params) { List> list = jdbcTemplate.query(sql, params, new CamelCaseRowMapper()); - long count = this.countQuery(countSql, params); return new PageImpl<>(list, pageRequest, count); } + public Page> queryForPage(String sql, PageRequest pageRequest, Object... params) { + String countSql = "select count(1) "+sql; + return this.queryForPage(sql, countSql, pageRequest, params); + } + private long countQuery(String sql, Object... params) { int paramsLength = params.length; diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/BaseRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/BaseRepository.java new file mode 100644 index 00000000..b4200b97 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/BaseRepository.java @@ -0,0 +1,16 @@ +package com.codingapi.springboot.fast.jpa.repository; + +import org.springframework.core.ResolvableType; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.NoRepositoryBean; + +@NoRepositoryBean +public interface BaseRepository extends JpaRepository { + + @SuppressWarnings("unchecked") + default Class getEntityClass() { + ResolvableType resolvableType = ResolvableType.forClass(getClass()).as(BaseRepository.class); + return (Class) resolvableType.getGeneric(0).resolve(); + } + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicNativeRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicNativeRepository.java new file mode 100644 index 00000000..1dd846b9 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicNativeRepository.java @@ -0,0 +1,50 @@ +package com.codingapi.springboot.fast.jpa.repository; + +import com.codingapi.springboot.fast.jdbc.JdbcQueryContext; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.repository.NoRepositoryBean; + +import java.util.List; +import java.util.Map; + +@NoRepositoryBean +public interface DynamicNativeRepository extends BaseRepository { + + default List> dynamicNativeListMapQuery(String sql, Object... params) { + return JdbcQueryContext.getInstance().getJdbcQuery().queryForList(sql, params); + } + + default List dynamicNativeListQuery(String sql, Object... params) { + return dynamicNativeListQuery(getEntityClass(), sql, params); + } + + default List dynamicNativeListQuery(Class clazz, String sql, Object... params) { + return JdbcQueryContext.getInstance().getJdbcQuery().queryForList(sql, clazz, params); + } + + default Page dynamicNativePageQuery(String sql, String countSql, PageRequest request, Object... params) { + return dynamicNativePageQuery(getEntityClass(), sql, countSql, request, params); + } + + default Page dynamicNativePageQuery(String sql, PageRequest request, Object... params) { + return dynamicNativePageQuery(getEntityClass(), sql, request, params); + } + + default Page dynamicNativePageQuery(Class clazz, String sql, String countSql, PageRequest request, Object... params) { + return JdbcQueryContext.getInstance().getJdbcQuery().queryForPage(sql, countSql, clazz, request, params); + } + + default Page dynamicNativePageQuery(Class clazz, String sql, PageRequest request, Object... params) { + return JdbcQueryContext.getInstance().getJdbcQuery().queryForPage(sql, clazz, request, params); + } + + default Page> dynamicNativePageMapQuery(String sql, String countSql, PageRequest request, Object... params) { + return JdbcQueryContext.getInstance().getJdbcQuery().queryForPage(sql, countSql, request, params); + } + + default Page> dynamicNativePageMapQuery(String sql, PageRequest request, Object... params) { + return JdbcQueryContext.getInstance().getJdbcQuery().queryForPage(sql, request, params); + } + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRepository.java index 992f8ab2..b8e0850c 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRepository.java @@ -1,22 +1,15 @@ package com.codingapi.springboot.fast.jpa.repository; import com.codingapi.springboot.fast.jpa.JpaQueryContext; -import org.springframework.core.ResolvableType; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; -import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.NoRepositoryBean; import java.util.List; @NoRepositoryBean @SuppressWarnings("unchecked") -public interface DynamicRepository extends JpaRepository { - - default Class getEntityClass() { - ResolvableType resolvableType = ResolvableType.forClass(this.getClass()).as(DynamicRepository.class); - return resolvableType.getGeneric(new int[]{0}).resolve(); - } +public interface DynamicRepository extends BaseRepository { default List dynamicListQuery(String sql, Object... params) { return (List) JpaQueryContext.getInstance().getJPAQuery().listQuery(getEntityClass(), sql, params); diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java index d3e2deb8..1cc0c6c0 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/FastRepository.java @@ -2,7 +2,6 @@ import com.codingapi.springboot.framework.dto.request.PageRequest; import com.codingapi.springboot.framework.dto.request.SearchRequest; -import org.springframework.core.ResolvableType; import org.springframework.data.domain.Page; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; @@ -10,32 +9,25 @@ /** * 更强大的Repository对象 + * * @param * @param */ @NoRepositoryBean -public interface FastRepository extends JpaRepository, JpaSpecificationExecutor, DynamicRepository { +public interface FastRepository extends JpaRepository, JpaSpecificationExecutor, DynamicRepository, DynamicNativeRepository { default Page findAll(PageRequest request) { if (request.hasFilter()) { - Class clazz = getDomainClass(); + Class clazz = getEntityClass(); ExampleBuilder exampleBuilder = new ExampleBuilder(request, clazz); return findAll(exampleBuilder.getExample(), request); } return findAll((org.springframework.data.domain.PageRequest) request); } - - @SuppressWarnings("unchecked") - default Class getDomainClass() { - ResolvableType resolvableType = ResolvableType.forClass(getClass()).as(FastRepository.class); - return (Class) resolvableType.getGeneric(0).resolve(); - } - - default Page pageRequest(PageRequest request) { if (request.hasFilter()) { - Class clazz = getDomainClass(); + Class clazz = getEntityClass(); DynamicSQLBuilder dynamicSQLBuilder = new DynamicSQLBuilder(request, clazz); return dynamicPageQuery(dynamicSQLBuilder.getHQL(), request, dynamicSQLBuilder.getParams()); } @@ -44,7 +36,7 @@ default Page pageRequest(PageRequest request) { default Page searchRequest(SearchRequest request) { - Class clazz = getDomainClass(); + Class clazz = getEntityClass(); return pageRequest(request.toPageRequest(clazz)); } diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 18424120..3cd99f08 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.8.4 + 2.8.5 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 4b79616a..d85057b3 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.8.4 + 2.8.5 springboot-starter From c48c61cf6ae110dcbdb3d409bd7e8c3b1388948d Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sat, 7 Sep 2024 10:02:30 +0800 Subject: [PATCH 046/101] add ClassLoaderUtils.java --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../framework/utils/ClassLoaderUtils.java | 99 +++++++++++++++++++ 5 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/utils/ClassLoaderUtils.java diff --git a/pom.xml b/pom.xml index acaafde1..e37ad7a4 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.8.5 + 2.8.6 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index c810d8e4..92f0e78a 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.8.5 + 2.8.6 4.0.0 diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 3cd99f08..044edc39 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.8.5 + 2.8.6 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index d85057b3..59fa2065 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.8.5 + 2.8.6 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/utils/ClassLoaderUtils.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/utils/ClassLoaderUtils.java new file mode 100644 index 00000000..df9cd18b --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/utils/ClassLoaderUtils.java @@ -0,0 +1,99 @@ +package com.codingapi.springboot.framework.utils; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +public class ClassLoaderUtils { + + public static URLClassLoader createClassLoader(String jarPath) throws MalformedURLException { + File file = new File(jarPath); + if(!file.exists()){ + throw new RuntimeException("jar file not found:"+jarPath); + } + URL[] urls = new URL[]{file.toURI().toURL()}; + return new URLClassLoader(urls, ClassLoader.getSystemClassLoader()); + } + + public static List findAllClasses(URLClassLoader classLoader) throws URISyntaxException, IOException { + List classNames = new ArrayList<>(); + URL[] urls = classLoader.getURLs(); + for (URL url : urls) { + if (url.getProtocol().equals("file")) { + File file = new File(url.toURI()); + if (file.isDirectory()) { + classNames.addAll(findClassesInDirectory(file, "")); + } else if (file.getName().endsWith(".jar")) { + classNames.addAll(findClassesInJar(new JarFile(file))); + } + } + } + return classNames; + } + + public static List> findJarClasses(String jarPath) throws IOException, URISyntaxException { + URLClassLoader classLoader = createClassLoader(jarPath); + List classList = ClassLoaderUtils.findAllClasses(classLoader); + List> classes = new ArrayList<>(); + for(String className:classList){ + try { + Class driverClass = classLoader.loadClass(className); + classes.add(driverClass); + }catch (NoClassDefFoundError | ClassNotFoundException ignored){} + } + return classes; + } + + public static List> findJarClass(String jarPath,Class clazz) throws IOException, URISyntaxException { + URLClassLoader classLoader = createClassLoader(jarPath); + List classList = ClassLoaderUtils.findAllClasses(classLoader); + List> classes = new ArrayList<>(); + for(String className:classList){ + try { + Class driverClass = classLoader.loadClass(className); + if(clazz.isAssignableFrom(driverClass)){ + classes.add(driverClass); + } + }catch (NoClassDefFoundError | ClassNotFoundException ignored){} + } + return classes; + } + + private static List findClassesInDirectory(File directory, String packageName) { + List classNames = new ArrayList<>(); + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + classNames.addAll(findClassesInDirectory(file, packageName + file.getName() + ".")); + } else if (file.getName().endsWith(".class")) { + String className = packageName + file.getName().replace(".class", ""); + classNames.add(className); + } + } + } + return classNames; + } + + private static List findClassesInJar(JarFile jarFile) { + List classNames = new ArrayList<>(); + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String name = entry.getName(); + if (name.endsWith(".class")) { + String className = name.replace('/', '.').replace(".class", ""); + classNames.add(className); + } + } + return classNames; + } +} From ba5fdbdfc985619516c356fcaba4d5e7f98f2e03 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sat, 21 Sep 2024 18:38:13 +0800 Subject: [PATCH 047/101] #50 --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../framework/dto/request/PageRequest.java | 66 ++++--------------- 5 files changed, 17 insertions(+), 57 deletions(-) diff --git a/pom.xml b/pom.xml index e37ad7a4..9eb6033d 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.8.6 + 2.8.7 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 92f0e78a..cc37892f 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.8.6 + 2.8.7 4.0.0 diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 044edc39..5d5a415c 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.8.6 + 2.8.7 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 59fa2065..b45f6506 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.8.6 + 2.8.7 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java index ff5b3df5..cf62e20e 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/PageRequest.java @@ -2,11 +2,8 @@ import lombok.Getter; import lombok.Setter; -import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import java.util.Optional; - public class PageRequest extends org.springframework.data.domain.PageRequest { @Getter @@ -14,20 +11,20 @@ public class PageRequest extends org.springframework.data.domain.PageRequest { private int current; @Setter - @Getter private int pageSize; @Getter - private final RequestFilter requestFilter = new RequestFilter(); + private Sort sort; + @Getter + private final RequestFilter requestFilter = new RequestFilter(); - private org.springframework.data.domain.PageRequest pageRequest; public PageRequest(int current, int pageSize, Sort sort) { super(current, pageSize, sort); this.current = current; this.pageSize = pageSize; - this.pageRequest = org.springframework.data.domain.PageRequest.of(current, pageSize, sort); + this.sort = sort; } @@ -39,6 +36,7 @@ public String getStringFilter(String key) { return requestFilter.getStringFilter(key); } + public String getStringFilter(String key, String defaultValue) { return requestFilter.getStringFilter(key, defaultValue); } @@ -47,33 +45,20 @@ public int getIntFilter(String key) { return requestFilter.getIntFilter(key); } + public int getIntFilter(String key, int defaultValue) { return requestFilter.getIntFilter(key, defaultValue); } + public boolean hasFilter() { return requestFilter.hasFilter(); } @Override - public Sort getSort() { - return pageRequest.getSort(); - } - - @Override - public PageRequest next() { - return new PageRequest(current + 1, getPageSize(), getSort()); - } - - @Override - public PageRequest previous() { - return current == 0 ? this : new PageRequest(current - 1, getPageSize(), getSort()); - } - - @Override - public PageRequest first() { - return new PageRequest(0, getPageSize(), getSort()); + public int getPageSize() { + return pageSize; } @Override @@ -91,37 +76,12 @@ public boolean hasPrevious() { return current > 0; } - @Override - public Pageable previousOrFirst() { - return pageRequest.previousOrFirst(); - } - - @Override - public boolean isPaged() { - return pageRequest.isPaged(); - } - - @Override - public boolean isUnpaged() { - return pageRequest.isUnpaged(); - } - - @Override - public Sort getSortOr(Sort sort) { - return pageRequest.getSortOr(sort); - } - - @Override - public Optional toOptional() { - return pageRequest.toOptional(); - } - public void addSort(Sort sort) { - Sort nowSort = pageRequest.getSort(); + Sort nowSort = this.sort; if (nowSort == Sort.unsorted()) { - this.pageRequest = new PageRequest(getCurrent(), getPageSize(), sort); + this.sort = sort; } else { - pageRequest.getSort().and(sort); + this.sort.and(sort); } } @@ -156,4 +116,4 @@ public static PageRequest of(int page, int size) { public static PageRequest of(int page, int size, Sort sort) { return new PageRequest(page, size, sort); } -} \ No newline at end of file +} From d0bb08a8794340504a1706aa1b218a911d9a7e75 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Thu, 26 Sep 2024 16:53:11 +0800 Subject: [PATCH 048/101] update 0.8.12 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9eb6033d..3724575f 100644 --- a/pom.xml +++ b/pom.xml @@ -269,7 +269,7 @@ org.jacoco jacoco-maven-plugin - 0.8.9 + 0.8.12 From 602ad595c5ad1424476d390dabab83f3a9a8f7fa Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Mon, 28 Oct 2024 09:01:19 +0800 Subject: [PATCH 049/101] update 2.8.8 --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- .../jpa/repository/DynamicSQLBuilder.java | 12 ++++++++- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../framework/dto/request/Filter.java | 10 ++++++++ .../framework/dto/request/Relation.java | 2 ++ .../framework/dto/request/SearchRequest.java | 25 +++++++++++++++---- 8 files changed, 47 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index 3724575f..399ade2d 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.8.7 + 2.8.8 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index cc37892f..24a2af7b 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.8.7 + 2.8.8 4.0.0 diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java index 06705ad8..caa1a9d7 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java @@ -103,6 +103,16 @@ private void buildSQL(Filter filter, StringBuilder hql) { params.add("%" + filter.getValue()[0] + "%"); paramIndex++; } + if (filter.isLeftLike()) { + hql.append(filter.getKey()).append(" LIKE ?").append(paramIndex); + params.add("%" + filter.getValue()[0]); + paramIndex++; + } + if (filter.isRightLike()) { + hql.append(filter.getKey()).append(" LIKE ?").append(paramIndex); + params.add(filter.getValue()[0] + "%"); + paramIndex++; + } if (filter.isIn()) { hql.append(filter.getKey()).append(" IN (").append("?").append(paramIndex).append(")"); params.add(Arrays.asList(filter.getValue())); @@ -140,4 +150,4 @@ private void buildSQL(Filter filter, StringBuilder hql) { public Object[] getParams() { return params.toArray(); } -} \ No newline at end of file +} diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 5d5a415c..e85fb3b3 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.8.7 + 2.8.8 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index b45f6506..84ab4cc5 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.8.7 + 2.8.8 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java index db6c5477..723448d5 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java @@ -52,6 +52,16 @@ public boolean isLike() { return relation == Relation.LIKE; } + + public boolean isLeftLike() { + return relation == Relation.LEFT_LIKE; + } + + public boolean isRightLike() { + return relation == Relation.RIGHT_LIKE; + } + + public boolean isBetween() { return relation == Relation.BETWEEN; } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Relation.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Relation.java index a5b3eaa0..7941a3c4 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Relation.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Relation.java @@ -4,6 +4,8 @@ public enum Relation { EQUAL, LIKE, + LEFT_LIKE, + RIGHT_LIKE, BETWEEN, IN, GREATER_THAN, diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java index b8fc5ae9..f559812b 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java @@ -137,19 +137,34 @@ public void addFilter(String key, List value) { } + private Class getKeyType(String key) { String[] keys = key.split("\\."); Class keyClass = clazz; + for (String k : keys) { - Field[] fields = keyClass.getDeclaredFields(); + keyClass = findFieldInHierarchy(keyClass, k); + + if (keyClass == null) { + throw new IllegalArgumentException("Field " + k + " not found in class hierarchy."); + } + } + return keyClass; + } + + private Class findFieldInHierarchy(Class clazz, String fieldName) { + Class currentClass = clazz; + + while (currentClass != null) { + Field[] fields = currentClass.getDeclaredFields(); for (Field field : fields) { - if (field.getName().equals(k)) { - keyClass = field.getType(); - break; + if (field.getName().equals(fieldName)) { + return field.getType(); } } + currentClass = currentClass.getSuperclass(); // 向上查找父类 } - return keyClass; + return null; } } From 555385185756af475c73beb7f71a2973a8974efe Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Thu, 7 Nov 2024 12:02:37 +0800 Subject: [PATCH 050/101] fix event bug --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../handler/ApplicationHandlerUtils.java | 38 ++++++------------- .../framework/handler/IHandler.java | 14 ++++++- 6 files changed, 27 insertions(+), 33 deletions(-) diff --git a/pom.xml b/pom.xml index 399ade2d..6982e4d3 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.8.8 + 2.8.9 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 24a2af7b..1ba56b09 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.8.8 + 2.8.9 4.0.0 diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index e85fb3b3..d4216971 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.8.8 + 2.8.9 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 84ab4cc5..cb6be28d 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.8.8 + 2.8.9 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/ApplicationHandlerUtils.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/ApplicationHandlerUtils.java index 7817ae56..a64c8fcc 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/ApplicationHandlerUtils.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/ApplicationHandlerUtils.java @@ -1,18 +1,14 @@ package com.codingapi.springboot.framework.handler; import com.codingapi.springboot.framework.event.IEvent; -import lombok.extern.slf4j.Slf4j; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; -@Slf4j class ApplicationHandlerUtils implements IHandler { private static ApplicationHandlerUtils instance; - private List> handlers; + private final List> handlers; private ApplicationHandlerUtils() { @@ -47,39 +43,27 @@ public void addHandler(IHandler handler) { @Override public void handler(IEvent event) { for (IHandler handler : handlers) { - String targetClassName = null; try { Class eventClass = event.getClass(); - Class targetClass = getHandlerEventClass(handler); + Class targetClass = handler.getHandlerEventClass(); if (eventClass.equals(targetClass)) { - targetClassName = targetClass.getName(); handler.handler(event); } } catch (Exception e) { - //IPersistenceEvent 抛出异常 - if ("com.codingapi.springboot.framework.persistence.PersistenceEvent".equals(targetClassName)) { - throw e; + Exception error = null; + try { + handler.error(e); + } catch (Exception err) { + error = err; } - log.warn("handler exception", e); - handler.error(e); - - } - } - } - - private Class getHandlerEventClass(IHandler handler) { - Type[] types = handler.getClass().getGenericInterfaces(); - for (Type type : types) { - if (type instanceof ParameterizedType) { - ParameterizedType parameterizedType = (ParameterizedType) type; - Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); - if (actualTypeArguments != null) { - return (Class) actualTypeArguments[0]; + if (error != null) { + throw new RuntimeException(error); } } } - return null; } + + } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/IHandler.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/IHandler.java index 45db5dd7..21cdecd8 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/IHandler.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/IHandler.java @@ -1,6 +1,7 @@ package com.codingapi.springboot.framework.handler; import com.codingapi.springboot.framework.event.IEvent; +import org.springframework.core.ResolvableType; /** * handler 订阅 @@ -21,9 +22,18 @@ public interface IHandler { * * @param exception 异常信息 */ - default void error(Exception exception) { + default void error(Exception exception) throws Exception{ + throw exception; + } + + + /** + * 获取订阅的事件类型 + */ + default Class getHandlerEventClass() { + ResolvableType resolvableType = ResolvableType.forClass(getClass()).as(IHandler.class); + return (Class) resolvableType.getGeneric(0).resolve(); } - ; } From cb501d582831d2999c30410ff8c2da94c86aba6e Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Thu, 7 Nov 2024 14:14:53 +0800 Subject: [PATCH 051/101] fix event bug --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 6982e4d3..b9e5aaf0 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.8.9 + 2.8.10 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 1ba56b09..4c095838 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.8.9 + 2.8.10 4.0.0 diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index d4216971..665982f1 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.8.9 + 2.8.10 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index cb6be28d..21915339 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.8.9 + 2.8.10 springboot-starter From b825aff2ac57498329b6d61893e40fc83fd1a9be Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sat, 9 Nov 2024 09:16:47 +0800 Subject: [PATCH 052/101] fix event bug --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../ApplicationHandlerUtils.java | 25 +++--- .../framework/event/DomainEvent.java | 8 +- .../framework/event/DomainEventContext.java | 4 +- .../framework/event/EventStackContext.java | 80 ++++++++++++++++++ .../framework/event/EventTraceContext.java | 81 +++++++++++++++++++ .../framework/{handler => event}/Handler.java | 2 +- .../HandlerBeanDefinitionRegistrar.java | 2 +- .../springboot/framework/event/IEvent.java | 4 +- .../{handler => event}/IHandler.java | 8 +- .../SpringEventHandler.java | 19 ++++- .../SpringHandlerConfiguration.java | 2 +- .../framework/exception/EventException.java | 17 ++++ .../exception/EventLoopException.java | 17 ++++ .../framework/utils/RandomGenerator.java | 22 +++++ .../main/resources/META-INF/spring.factories | 6 +- ...ot.autoconfigure.AutoConfiguration.imports | 6 +- .../handler/DemoChangeLogHandler.java | 2 + .../framework/handler/DemoCreateHandler.java | 2 + .../framework/handler/DemoDeleteHandler.java | 2 + .../handler/DemoPersistEventHandler.java | 4 +- .../handler/EntityFiledChangeHandler.java | 4 +- 25 files changed, 288 insertions(+), 37 deletions(-) rename springboot-starter/src/main/java/com/codingapi/springboot/framework/{handler => event}/ApplicationHandlerUtils.java (69%) create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/event/EventStackContext.java create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/event/EventTraceContext.java rename springboot-starter/src/main/java/com/codingapi/springboot/framework/{handler => event}/Handler.java (76%) rename springboot-starter/src/main/java/com/codingapi/springboot/framework/{handler => event}/HandlerBeanDefinitionRegistrar.java (96%) rename springboot-starter/src/main/java/com/codingapi/springboot/framework/{handler => event}/IHandler.java (73%) rename springboot-starter/src/main/java/com/codingapi/springboot/framework/{handler => event}/SpringEventHandler.java (64%) rename springboot-starter/src/main/java/com/codingapi/springboot/framework/{handler => event}/SpringHandlerConfiguration.java (89%) create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/exception/EventException.java create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/exception/EventLoopException.java create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/utils/RandomGenerator.java diff --git a/pom.xml b/pom.xml index b9e5aaf0..f93e9daf 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.8.10 + 2.8.11 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 4c095838..a2fc1cdf 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.8.10 + 2.8.11 4.0.0 diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 665982f1..11b91dab 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.8.10 + 2.8.11 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 21915339..ca03cc75 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.8.10 + 2.8.11 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/ApplicationHandlerUtils.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/ApplicationHandlerUtils.java similarity index 69% rename from springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/ApplicationHandlerUtils.java rename to springboot-starter/src/main/java/com/codingapi/springboot/framework/event/ApplicationHandlerUtils.java index a64c8fcc..0e97bc4a 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/ApplicationHandlerUtils.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/ApplicationHandlerUtils.java @@ -1,6 +1,7 @@ -package com.codingapi.springboot.framework.handler; +package com.codingapi.springboot.framework.event; -import com.codingapi.springboot.framework.event.IEvent; +import com.codingapi.springboot.framework.exception.EventException; +import com.codingapi.springboot.framework.exception.EventLoopException; import java.util.ArrayList; import java.util.List; @@ -42,28 +43,32 @@ public void addHandler(IHandler handler) { @Override public void handler(IEvent event) { + Class eventClass = event.getClass(); + List errorStack = new ArrayList<>(); + boolean throwException = false; for (IHandler handler : handlers) { try { - Class eventClass = event.getClass(); Class targetClass = handler.getHandlerEventClass(); if (eventClass.equals(targetClass)) { handler.handler(event); } } catch (Exception e) { - Exception error = null; + if (e instanceof EventLoopException) { + throw e; + } try { handler.error(e); + errorStack.add(e); } catch (Exception err) { - error = err; - } - if (error != null) { - throw new RuntimeException(error); + throwException = true; + errorStack.add(err); } } } + if(throwException){ + throw new EventException(errorStack); + } } - - } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/DomainEvent.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/DomainEvent.java index 1f478ac1..a03d694e 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/DomainEvent.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/DomainEvent.java @@ -18,10 +18,14 @@ public class DomainEvent extends ApplicationEvent { @Getter private final boolean sync; - public DomainEvent(Object source, boolean sync) { + @Getter + private final String traceId; + + + public DomainEvent(Object source, boolean sync, String traceId) { super(source); this.event = (IEvent) source; this.sync = sync; + this.traceId = traceId; } - } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/DomainEventContext.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/DomainEventContext.java index f7bcd7c2..a22799e2 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/DomainEventContext.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/DomainEventContext.java @@ -25,7 +25,9 @@ public static DomainEventContext getInstance() { private void push(IEvent event, boolean sync) { if (context != null) { - context.publishEvent(new DomainEvent(event, sync)); + String traceId = EventTraceContext.getInstance().getOrCreateTrace(); + EventTraceContext.getInstance().addEvent(traceId,event); + context.publishEvent(new DomainEvent(event, sync,traceId)); } } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/EventStackContext.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/EventStackContext.java new file mode 100644 index 00000000..bcc01407 --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/EventStackContext.java @@ -0,0 +1,80 @@ +package com.codingapi.springboot.framework.event; + +import lombok.Getter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 事件栈上下文 + */ +public class EventStackContext { + + private final Map>> eventClassStack = new HashMap<>(); + private final Map> eventStack = new HashMap<>(); + + @Getter + private final static EventStackContext instance = new EventStackContext(); + + private EventStackContext() { + + } + + private void addEventClass(String traceId, IEvent event) { + List> events = eventClassStack.get(traceId); + if (events == null) { + events = new ArrayList<>(); + } + events.add(event.getClass()); + eventClassStack.put(traceId, events); + } + + private void addEventStack(String traceId, IEvent event) { + List events = eventStack.get(traceId); + if (events == null) { + events = new ArrayList<>(); + } + events.add(event); + eventStack.put(traceId, events); + } + + + void addEvent(String traceId, IEvent event) { + addEventClass(traceId, event); + addEventStack(traceId, event); + } + + boolean checkEventLoop(String traceId, IEvent event) { + List> events = eventClassStack.get(traceId); + if (events != null) { + return events.contains(event.getClass()); + } + return false; + } + + public List getEvents(String eventKey) { + if(eventKey!=null) { + String traceId = eventKey.split("#")[0]; + return eventStack.get(traceId); + } + return null; + } + + public List> getEventClasses(String eventKey) { + if(eventKey!=null) { + String traceId = eventKey.split("#")[0]; + return eventClassStack.get(traceId); + } + return null; + } + + + void remove(String traceId) { + eventStack.remove(traceId); + eventClassStack.remove(traceId); + } + + +} diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/EventTraceContext.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/EventTraceContext.java new file mode 100644 index 00000000..844d2cf9 --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/EventTraceContext.java @@ -0,0 +1,81 @@ +package com.codingapi.springboot.framework.event; + +import com.codingapi.springboot.framework.exception.EventLoopException; +import com.codingapi.springboot.framework.utils.RandomGenerator; +import lombok.Getter; + +import java.util.*; + +/** + * 事件跟踪上下文 + */ +public class EventTraceContext { + + @Getter + private final static EventTraceContext instance = new EventTraceContext(); + + // trace key + private final Set traceKeys = new HashSet<>(); + + // thread local + private final ThreadLocal threadLocal = new ThreadLocal<>(); + + // event listenerKey state + private final Map eventKeyState = new HashMap<>(); + + + private EventTraceContext() { + } + + String getOrCreateTrace() { + String eventKey = threadLocal.get(); + if (eventKey != null) { + return eventKey.split("#")[0]; + } + String traceId = UUID.randomUUID().toString().replaceAll("-", ""); + traceKeys.add(traceId); + return traceId; + } + + /** + * get event key + * traceId = eventKey.split("#")[0] + */ + public String getEventKey() { + return threadLocal.get(); + } + + void createEventKey(String traceId) { + String eventKey = traceId + "#" + RandomGenerator.randomString(8); + eventKeyState.put(eventKey, false); + threadLocal.set(eventKey); + } + + void checkEventState() { + String eventKey = threadLocal.get(); + if (eventKey != null) { + boolean state = eventKeyState.get(eventKey); + if (!state) { + // event execute finish + String traceId = eventKey.split("#")[0]; + traceKeys.remove(traceId); + EventStackContext.getInstance().remove(traceId); + } + eventKeyState.remove(eventKey); + } + threadLocal.remove(); + } + + void addEvent(String traceId, IEvent event) { + boolean hasEventLoop = EventStackContext.getInstance().checkEventLoop(traceId, event); + if (hasEventLoop) { + List> stack = EventStackContext.getInstance().getEventClasses(traceId); + traceKeys.remove(traceId); + EventStackContext.getInstance().remove(traceId); + eventKeyState.remove(traceId); + threadLocal.remove(); + throw new EventLoopException(stack, event); + } + EventStackContext.getInstance().addEvent(traceId, event); + } +} diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/Handler.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/Handler.java similarity index 76% rename from springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/Handler.java rename to springboot-starter/src/main/java/com/codingapi/springboot/framework/event/Handler.java index d4321a03..6dda24a0 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/Handler.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/Handler.java @@ -1,4 +1,4 @@ -package com.codingapi.springboot.framework.handler; +package com.codingapi.springboot.framework.event; import java.lang.annotation.*; diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/HandlerBeanDefinitionRegistrar.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/HandlerBeanDefinitionRegistrar.java similarity index 96% rename from springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/HandlerBeanDefinitionRegistrar.java rename to springboot-starter/src/main/java/com/codingapi/springboot/framework/event/HandlerBeanDefinitionRegistrar.java index 3f1a76b3..de878c8b 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/HandlerBeanDefinitionRegistrar.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/HandlerBeanDefinitionRegistrar.java @@ -1,4 +1,4 @@ -package com.codingapi.springboot.framework.handler; +package com.codingapi.springboot.framework.event; import com.codingapi.springboot.framework.registrar.RegisterBeanScanner; import lombok.SneakyThrows; diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/IEvent.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/IEvent.java index ca03ea12..327ca279 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/IEvent.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/IEvent.java @@ -1,6 +1,8 @@ package com.codingapi.springboot.framework.event; +import java.io.Serializable; + /** * 默认同步事件 *

@@ -8,7 +10,7 @@ * 事件本身不应该同步主业务的事务,即事件对于主业务来说,可成功可失败,成功与失败都不应该强关联主体业务。 * 若需要让主体业务与分支做事务同步的时候,那不应该采用事件机制,而应该直接采用调用的方式实现业务绑定。 */ -public interface IEvent { +public interface IEvent extends Serializable { } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/IHandler.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/IHandler.java similarity index 73% rename from springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/IHandler.java rename to springboot-starter/src/main/java/com/codingapi/springboot/framework/event/IHandler.java index 21cdecd8..226ee17d 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/IHandler.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/IHandler.java @@ -1,6 +1,5 @@ -package com.codingapi.springboot.framework.handler; +package com.codingapi.springboot.framework.event; -import com.codingapi.springboot.framework.event.IEvent; import org.springframework.core.ResolvableType; /** @@ -19,10 +18,11 @@ public interface IHandler { /** * 异常回掉,在多订阅的情况下,为了实现订阅的独立性,将异常的处理放在回掉函数中。 + * 当异常抛出以后,会阻止后续的事件执行 * * @param exception 异常信息 */ - default void error(Exception exception) throws Exception{ + default void error(Exception exception) throws Exception { throw exception; } @@ -32,7 +32,7 @@ default void error(Exception exception) throws Exception{ */ default Class getHandlerEventClass() { ResolvableType resolvableType = ResolvableType.forClass(getClass()).as(IHandler.class); - return (Class) resolvableType.getGeneric(0).resolve(); + return resolvableType.getGeneric(0).resolve(); } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/SpringEventHandler.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/SpringEventHandler.java similarity index 64% rename from springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/SpringEventHandler.java rename to springboot-starter/src/main/java/com/codingapi/springboot/framework/event/SpringEventHandler.java index 9796511b..5ec7b9ba 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/SpringEventHandler.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/SpringEventHandler.java @@ -1,6 +1,5 @@ -package com.codingapi.springboot.framework.handler; +package com.codingapi.springboot.framework.event; -import com.codingapi.springboot.framework.event.DomainEvent; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationListener; @@ -28,11 +27,23 @@ public SpringEventHandler(List handlers) { @Override public void onApplicationEvent(DomainEvent domainEvent) { + String traceId = domainEvent.getTraceId(); + if (domainEvent.isSync()) { - ApplicationHandlerUtils.getInstance().handler(domainEvent.getEvent()); + try { + EventTraceContext.getInstance().createEventKey(traceId); + ApplicationHandlerUtils.getInstance().handler(domainEvent.getEvent()); + } finally { + EventTraceContext.getInstance().checkEventState(); + } } else { executorService.execute(() -> { - ApplicationHandlerUtils.getInstance().handler(domainEvent.getEvent()); + try { + EventTraceContext.getInstance().createEventKey(traceId); + ApplicationHandlerUtils.getInstance().handler(domainEvent.getEvent()); + } finally { + EventTraceContext.getInstance().checkEventState(); + } }); } } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/SpringHandlerConfiguration.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/SpringHandlerConfiguration.java similarity index 89% rename from springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/SpringHandlerConfiguration.java rename to springboot-starter/src/main/java/com/codingapi/springboot/framework/event/SpringHandlerConfiguration.java index 0f5c989c..e3afeb12 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/handler/SpringHandlerConfiguration.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/SpringHandlerConfiguration.java @@ -1,4 +1,4 @@ -package com.codingapi.springboot.framework.handler; +package com.codingapi.springboot.framework.event; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/exception/EventException.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/exception/EventException.java new file mode 100644 index 00000000..68462e73 --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/exception/EventException.java @@ -0,0 +1,17 @@ +package com.codingapi.springboot.framework.exception; + +import lombok.Getter; + +import java.util.List; +import java.util.stream.Collectors; + +@Getter +public class EventException extends RuntimeException { + + private final List error; + + public EventException(List error) { + super(error.stream().map(Exception::getMessage).collect(Collectors.joining("\n"))); + this.error = error; + } +} diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/exception/EventLoopException.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/exception/EventLoopException.java new file mode 100644 index 00000000..cb17f046 --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/exception/EventLoopException.java @@ -0,0 +1,17 @@ +package com.codingapi.springboot.framework.exception; + +import com.codingapi.springboot.framework.event.IEvent; +import lombok.Getter; + +import java.util.List; + +@Getter +public class EventLoopException extends RuntimeException { + + private final List> stack; + + public EventLoopException(List> stack, IEvent event) { + super("event loop error current event class:" + event.getClass() + ", history event stack:" + stack); + this.stack = stack; + } +} diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/utils/RandomGenerator.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/utils/RandomGenerator.java new file mode 100644 index 00000000..d81723c6 --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/utils/RandomGenerator.java @@ -0,0 +1,22 @@ +package com.codingapi.springboot.framework.utils; + +import java.util.UUID; + +public class RandomGenerator { + + public static String generateUUID() { + return UUID.randomUUID().toString(); + } + + + public static String randomString(int length) { + String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) { + int number = (int) (Math.random() * str.length()); + sb.append(str.charAt(number)); + } + return sb.toString(); + } + +} diff --git a/springboot-starter/src/main/resources/META-INF/spring.factories b/springboot-starter/src/main/resources/META-INF/spring.factories index 5765e102..3fd6c549 100644 --- a/springboot-starter/src/main/resources/META-INF/spring.factories +++ b/springboot-starter/src/main/resources/META-INF/spring.factories @@ -2,6 +2,6 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.codingapi.springboot.framework.AutoConfiguration,\ com.codingapi.springboot.framework.event.SpringEventConfiguration,\ com.codingapi.springboot.framework.exception.ExceptionConfiguration,\ -com.codingapi.springboot.framework.handler.HandlerBeanDefinitionRegistrar,\ -com.codingapi.springboot.framework.handler.SpringHandlerConfiguration,\ -com.codingapi.springboot.framework.servlet.BasicHandlerExceptionResolverConfiguration \ No newline at end of file +com.codingapi.springboot.framework.event.HandlerBeanDefinitionRegistrar,\ +com.codingapi.springboot.framework.event.SpringHandlerConfiguration,\ +com.codingapi.springboot.framework.servlet.BasicHandlerExceptionResolverConfiguration diff --git a/springboot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/springboot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 39319e71..8aa6f4cd 100644 --- a/springboot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/springboot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,6 +1,6 @@ com.codingapi.springboot.framework.AutoConfiguration com.codingapi.springboot.framework.event.SpringEventConfiguration com.codingapi.springboot.framework.exception.ExceptionConfiguration -com.codingapi.springboot.framework.handler.HandlerBeanDefinitionRegistrar -com.codingapi.springboot.framework.handler.SpringHandlerConfiguration -com.codingapi.springboot.framework.servlet.BasicHandlerExceptionResolverConfiguration \ No newline at end of file +com.codingapi.springboot.framework.event.HandlerBeanDefinitionRegistrar +com.codingapi.springboot.framework.event.SpringHandlerConfiguration +com.codingapi.springboot.framework.servlet.BasicHandlerExceptionResolverConfiguration diff --git a/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/DemoChangeLogHandler.java b/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/DemoChangeLogHandler.java index 0f2d3bb1..f1efb83b 100644 --- a/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/DemoChangeLogHandler.java +++ b/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/DemoChangeLogHandler.java @@ -1,6 +1,8 @@ package com.codingapi.springboot.framework.handler; import com.codingapi.springboot.framework.event.DemoChangeEvent; +import com.codingapi.springboot.framework.event.Handler; +import com.codingapi.springboot.framework.event.IHandler; import lombok.extern.slf4j.Slf4j; @Slf4j diff --git a/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/DemoCreateHandler.java b/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/DemoCreateHandler.java index f48c9fcc..01ab9aa5 100644 --- a/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/DemoCreateHandler.java +++ b/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/DemoCreateHandler.java @@ -1,6 +1,8 @@ package com.codingapi.springboot.framework.handler; import com.codingapi.springboot.framework.domain.event.DomainCreateEvent; +import com.codingapi.springboot.framework.event.Handler; +import com.codingapi.springboot.framework.event.IHandler; import lombok.extern.slf4j.Slf4j; @Slf4j diff --git a/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/DemoDeleteHandler.java b/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/DemoDeleteHandler.java index 89885b16..6f02be80 100644 --- a/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/DemoDeleteHandler.java +++ b/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/DemoDeleteHandler.java @@ -1,6 +1,8 @@ package com.codingapi.springboot.framework.handler; import com.codingapi.springboot.framework.domain.event.DomainDeleteEvent; +import com.codingapi.springboot.framework.event.Handler; +import com.codingapi.springboot.framework.event.IHandler; import lombok.extern.slf4j.Slf4j; @Slf4j diff --git a/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/DemoPersistEventHandler.java b/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/DemoPersistEventHandler.java index cb3dae62..2d18934f 100644 --- a/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/DemoPersistEventHandler.java +++ b/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/DemoPersistEventHandler.java @@ -1,11 +1,13 @@ package com.codingapi.springboot.framework.handler; import com.codingapi.springboot.framework.domain.event.DomainPersistEvent; +import com.codingapi.springboot.framework.event.Handler; +import com.codingapi.springboot.framework.event.IHandler; import lombok.extern.slf4j.Slf4j; @Slf4j @Handler -public class DemoPersistEventHandler implements IHandler{ +public class DemoPersistEventHandler implements IHandler { @Override public void handler(DomainPersistEvent event) { diff --git a/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/EntityFiledChangeHandler.java b/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/EntityFiledChangeHandler.java index b6bf661f..d4fb12b0 100644 --- a/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/EntityFiledChangeHandler.java +++ b/springboot-starter/src/test/java/com/codingapi/springboot/framework/handler/EntityFiledChangeHandler.java @@ -1,11 +1,13 @@ package com.codingapi.springboot.framework.handler; import com.codingapi.springboot.framework.domain.event.DomainChangeEvent; +import com.codingapi.springboot.framework.event.Handler; +import com.codingapi.springboot.framework.event.IHandler; import lombok.extern.slf4j.Slf4j; @Slf4j @Handler -public class EntityFiledChangeHandler implements IHandler{ +public class EntityFiledChangeHandler implements IHandler { @Override public void handler(DomainChangeEvent event) { From bae16217bef88c749159a986b61195ec497082c9 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sat, 9 Nov 2024 10:53:22 +0800 Subject: [PATCH 053/101] add flow & update event --- pom.xml | 41 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/README.md | 39 + springboot-starter-flow/pom.xml | 52 ++ .../springboot/flow/FlowConfiguration.java | 17 + .../flow/FlowFrameworkRegister.java | 17 + .../flow/bind/BindDataSnapshot.java | 55 ++ .../springboot/flow/bind/IBindData.java | 18 + .../flow/build/FlowWorkBuilder.java | 123 +++ .../springboot/flow/build/SchemaReader.java | 92 ++ .../springboot/flow/content/FlowSession.java | 77 ++ .../flow/content/FlowSessionBeanProvider.java | 30 + .../springboot/flow/domain/FlowNode.java | 303 +++++++ .../springboot/flow/domain/FlowRelation.java | 168 ++++ .../springboot/flow/domain/FlowWork.java | 332 +++++++ .../springboot/flow/domain/Opinion.java | 78 ++ .../springboot/flow/em/ApprovalType.java | 26 + .../flow/em/FlowSourceDirection.java | 30 + .../springboot/flow/em/FlowStatus.java | 27 + .../springboot/flow/em/FlowType.java | 30 + .../springboot/flow/em/NodeType.java | 30 + .../springboot/flow/error/ErrTrigger.java | 36 + .../springboot/flow/error/ErrorResult.java | 17 + .../springboot/flow/error/NodeResult.java | 16 + .../springboot/flow/error/OperatorResult.java | 25 + .../flow/event/FlowApprovalEvent.java | 88 ++ .../flow/generator/TitleGenerator.java | 47 + .../flow/matcher/OperatorMatcher.java | 115 +++ .../springboot/flow/pojo/FlowDetail.java | 95 ++ .../springboot/flow/pojo/FlowResult.java | 26 + .../flow/query/FlowRecordQuery.java | 55 ++ .../springboot/flow/record/FlowBackup.java | 61 ++ .../springboot/flow/record/FlowProcess.java | 42 + .../springboot/flow/record/FlowRecord.java | 417 +++++++++ .../flow/repository/FlowBackupRepository.java | 33 + .../repository/FlowBindDataRepository.java | 31 + .../repository/FlowOperatorRepository.java | 29 + .../repository/FlowProcessRepository.java | 16 + .../flow/repository/FlowRecordRepository.java | 70 ++ .../flow/repository/FlowWorkRepository.java | 18 + .../flow/script/GroovyShellContext.java | 87 ++ .../serializable/FlowNodeSerializable.java | 94 ++ .../FlowRelationSerializable.java | 82 ++ .../serializable/FlowWorkSerializable.java | 140 +++ .../flow/service/FlowDirectionService.java | 126 +++ .../service/FlowRecordBuilderService.java | 251 ++++++ .../flow/service/FlowRecordService.java | 192 ++++ .../springboot/flow/service/FlowService.java | 522 +++++++++++ .../springboot/flow/trigger/OutTrigger.java | 44 + .../springboot/flow/user/IFlowOperator.java | 36 + .../springboot/flow/utils/Sha256Utils.java | 25 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + .../springboot/flow/FlowTestApplication.java | 12 + .../codingapi/springboot/flow/flow/Leave.java | 24 + .../repository/FlowBackupRepositoryImpl.java | 29 + .../FlowBindDataRepositoryImpl.java | 38 + .../repository/FlowProcessRepositoryImpl.java | 39 + .../repository/FlowRecordRepositoryImpl.java | 104 +++ .../repository/FlowWorkRepositoryImpl.java | 34 + .../flow/repository/LeaveRepository.java | 18 + .../flow/repository/UserRepository.java | 39 + .../flow/script/GroovyShellContextTest.java | 30 + .../springboot/flow/test/BuildTest.java | 49 ++ .../springboot/flow/test/ErrorTest.java | 218 +++++ .../springboot/flow/test/FlowTest.java | 821 ++++++++++++++++++ .../springboot/flow/test/FlowTest2.java | 113 +++ .../flow/test/MultiRelationFlowTest.java | 314 +++++++ .../springboot/flow/test/QueryTest.java | 540 ++++++++++++ .../springboot/flow/test/ScriptBuildTest.java | 43 + .../springboot/flow/test/ScriptTest.java | 72 ++ .../springboot/flow/test/SignTest.java | 415 +++++++++ .../codingapi/springboot/flow/user/User.java | 46 + springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../event/ApplicationHandlerUtils.java | 38 +- .../springboot/framework/event/IEvent.java | 1 - .../springboot/framework/event/IHandler.java | 17 +- 77 files changed, 7452 insertions(+), 30 deletions(-) create mode 100644 springboot-starter-flow/README.md create mode 100644 springboot-starter-flow/pom.xml create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/FlowConfiguration.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/FlowFrameworkRegister.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/BindDataSnapshot.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/IBindData.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/FlowWorkBuilder.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/SchemaReader.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSessionBeanProvider.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowRelation.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowWork.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/Opinion.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/ApprovalType.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowSourceDirection.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowStatus.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowType.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/NodeType.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/error/ErrTrigger.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/error/ErrorResult.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/error/NodeResult.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/error/OperatorResult.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/event/FlowApprovalEvent.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/generator/TitleGenerator.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/matcher/OperatorMatcher.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowResult.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/query/FlowRecordQuery.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowBackup.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowProcess.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowBackupRepository.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowBindDataRepository.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowOperatorRepository.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowProcessRepository.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowWorkRepository.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/script/GroovyShellContext.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowNodeSerializable.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowRelationSerializable.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowWorkSerializable.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordBuilderService.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordService.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/trigger/OutTrigger.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/user/IFlowOperator.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/utils/Sha256Utils.java create mode 100644 springboot-starter-flow/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/FlowTestApplication.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowBackupRepositoryImpl.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowBindDataRepositoryImpl.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowProcessRepositoryImpl.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowWorkRepositoryImpl.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/LeaveRepository.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/UserRepository.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/script/GroovyShellContextTest.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/BuildTest.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ErrorTest.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/MultiRelationFlowTest.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/QueryTest.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptBuildTest.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptTest.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/SignTest.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/user/User.java diff --git a/pom.xml b/pom.xml index f93e9daf..ceb40070 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.8.11 + 2.9.0 https://github.com/codingapi/springboot-framewrok springboot-parent @@ -31,18 +31,20 @@ 1.6.13 3.1.0 ${project.version} - 2.0.42 - 0.12.5 - 2.15.0 + 2.0.53 + 0.12.6 + 2.17.0 + 3.17.0 1.8.1 - 1.11.0 + 1.12.0 0.10.2 0.9.16 - 1.77 + 1.79 1.2.0 2.2 - 4.0.15 - 2.2.224 + 4.0.24 + 2.3.232 + 5.6.2 @@ -75,6 +77,12 @@ + + com.esotericsoftware + kryo + ${esotericsoftware.kryo.version} + + com.h2database h2 @@ -99,6 +107,18 @@ ${commons-crypto.version} + + commons-io + commons-io + ${commons-io.version} + + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + org.perf4j perf4j @@ -249,6 +269,7 @@ springboot-starter + springboot-starter-flow springboot-starter-security springboot-starter-data-fast @@ -260,6 +281,7 @@ springboot-starter + springboot-starter-flow springboot-starter-security springboot-starter-data-fast @@ -289,7 +311,7 @@ org.openclover clover-maven-plugin - 4.4.1 + 4.5.2 true true @@ -309,6 +331,7 @@ springboot-starter + springboot-starter-flow springboot-starter-security springboot-starter-data-fast diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index a2fc1cdf..012a2759 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.8.11 + 2.9.0 4.0.0 diff --git a/springboot-starter-flow/README.md b/springboot-starter-flow/README.md new file mode 100644 index 00000000..0bea8953 --- /dev/null +++ b/springboot-starter-flow/README.md @@ -0,0 +1,39 @@ +# springboot-starter-flow 流程引擎 + +流程引擎支持的功能需要包括: + +支持的功能如下: + +流程管理 +1. build模式的流程设计 +2. schema模式的流程设计 +3. 流程的启用与禁用 +4. 流程快照的存储 + +流程设计 +1. 支持自定义节点与节点关系 +2. 支持自定义节点的操作用户,可通过groovy脚本定义 +3. 支持流程消息标题的自定义能力,可通过groovy脚本定义 +4. 支持流程异常状态的自定义能力,可通过groovy脚本定义 +5. 提供流程操作过程中的事件,可以做业务定制与延伸 + + +流程能力 +1. 流程发起 + 在设计完成以后并启用以后,可通过FlowService对象发起流程。 +2. 流程审批 + 流程的审批支持同意与拒绝,以及审批意见的填写。 +3. 流程撤销 + 流程的发起以后,在下一节点的流程待审批且未读之前可以撤销流程。 +4. 流程转办 + 流程的审批过程中,可以将流程转办给其他人员审批。 +5. 流程委托 + 可设置用户的委托人,委托人可以代理委托人审批流程。 +6. 流程催办 + 流程的审批过程中,可以催办审批人员,催办将会发送催办事件消息。 +7. 流程查询 + 可以查询流程的待办、已办、超时、延期、全部流程等数据。 +8. 流程干预 + 设置流程管理员的人员,可以对流程进行干预,可以直接对其他人的流程进行审批。 +9. 流程延期 + 流程的审批过程中,可以延期流程的审批时间。 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml new file mode 100644 index 00000000..c2d61cc1 --- /dev/null +++ b/springboot-starter-flow/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + springboot-parent + com.codingapi.springboot + 2.9.0 + + + springboot-starter-flow + springboot-starter-flow project for Spring Boot + springboot-starter-flow + + + 8 + + + + + + + com.codingapi.springboot + springboot-starter + + + + com.esotericsoftware + kryo + + + + org.apache.groovy + groovy + + + + org.apache.groovy + groovy-json + + + + org.apache.groovy + groovy-xml + + + + + + + diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/FlowConfiguration.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/FlowConfiguration.java new file mode 100644 index 00000000..7948386e --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/FlowConfiguration.java @@ -0,0 +1,17 @@ +package com.codingapi.springboot.flow; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class FlowConfiguration { + + @Bean + @ConditionalOnMissingBean + public FlowFrameworkRegister flowFrameworkRegister(ApplicationContext spring) { + return new FlowFrameworkRegister(spring); + } + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/FlowFrameworkRegister.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/FlowFrameworkRegister.java new file mode 100644 index 00000000..5fb46b8f --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/FlowFrameworkRegister.java @@ -0,0 +1,17 @@ +package com.codingapi.springboot.flow; + +import com.codingapi.springboot.flow.content.FlowSessionBeanProvider; +import lombok.AllArgsConstructor; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; + +@AllArgsConstructor +public class FlowFrameworkRegister implements InitializingBean { + + private final ApplicationContext application; + + @Override + public void afterPropertiesSet() throws Exception { + FlowSessionBeanProvider.getInstance().register(application); + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/BindDataSnapshot.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/BindDataSnapshot.java new file mode 100644 index 00000000..b2d0b1a8 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/BindDataSnapshot.java @@ -0,0 +1,55 @@ +package com.codingapi.springboot.flow.bind; + +import com.alibaba.fastjson.JSONObject; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +/** + * 数据快照 + */ +@Setter +@Getter +@AllArgsConstructor +public class BindDataSnapshot { + + /** + * 数据快照id + */ + private long id; + /** + * 快照信息 + */ + private String snapshot; + /** + * 创建时间 + */ + private long createTime; + + /** + * 数据绑定类名称 + */ + private String clazzName; + + public BindDataSnapshot(long id,IBindData bindData) { + if (bindData == null) { + throw new IllegalArgumentException("bind data is null"); + } + this.snapshot = bindData.toJsonSnapshot(); + this.clazzName = bindData.getClass().getName(); + this.createTime = System.currentTimeMillis(); + this.id = id; + } + + public BindDataSnapshot(IBindData bindData) { + this(0,bindData); + } + + public IBindData toBindData() { + try { + return JSONObject.parseObject(snapshot, (Class) Class.forName(clazzName)); + } catch (Exception e) { + throw new IllegalArgumentException("bind data error"); + } + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/IBindData.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/IBindData.java new file mode 100644 index 00000000..6a7287bc --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/IBindData.java @@ -0,0 +1,18 @@ +package com.codingapi.springboot.flow.bind; + +import com.alibaba.fastjson.JSONObject; + +/** + * 数据绑定接口 + */ +public interface IBindData { + + /** + * 数据快照 + * + * @return 数据快照 + */ + default String toJsonSnapshot() { + return JSONObject.toJSONString(this); + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/FlowWorkBuilder.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/FlowWorkBuilder.java new file mode 100644 index 00000000..08d89bf5 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/FlowWorkBuilder.java @@ -0,0 +1,123 @@ +package com.codingapi.springboot.flow.build; + +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowRelation; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.em.NodeType; +import com.codingapi.springboot.flow.error.ErrTrigger; +import com.codingapi.springboot.flow.generator.TitleGenerator; +import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.trigger.OutTrigger; +import com.codingapi.springboot.flow.user.IFlowOperator; +import com.codingapi.springboot.framework.utils.RandomGenerator; + +/** + * 流程工作构建器 + */ +public class FlowWorkBuilder { + + private FlowWork work = null; + + private FlowWorkBuilder(FlowWork flowWork) { + this.work = flowWork; + } + + + public static FlowWorkBuilder builder(IFlowOperator flowOperator) { + return new FlowWorkBuilder(new FlowWork(flowOperator)); + } + + public FlowWorkBuilder description(String description) { + this.work.setDescription(description); + return this; + } + + public FlowWorkBuilder postponedMax(int postponedMax) { + this.work.setPostponedMax(postponedMax); + return this; + } + + public FlowWorkBuilder title(String title) { + this.work.setTitle(title); + return this; + } + + public FlowWorkBuilder schema(String schema) { + this.work.schema(schema); + return this; + } + + + public Nodes nodes() { + return new Nodes(); + } + + public Relations relations() { + return new Relations(); + } + + public FlowWork build() { + work.enable(); + return work; + } + + + public class Nodes { + + public Nodes node(String id,String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, long timeout, TitleGenerator titleGenerator, ErrTrigger errTrigger, boolean editable) { + FlowNode node = new FlowNode(id, name, code, view, NodeType.parser(code), approvalType, titleGenerator, operatorMatcher, timeout, errTrigger, editable); + work.addNode(node); + return this; + } + + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher,long timeout, boolean editable) { + return node(RandomGenerator.generateUUID(),name, code, view, approvalType, operatorMatcher, timeout, TitleGenerator.defaultTitleGenerator(), null, editable); + } + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, boolean editable) { + return node(RandomGenerator.generateUUID(),name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), null, editable); + } + + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher) { + return node(name, code, view, approvalType, operatorMatcher, true); + } + + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, ErrTrigger errTrigger, boolean editable) { + return node(RandomGenerator.generateUUID(),name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), errTrigger, editable); + } + + + public Relations relations() { + return new Relations(); + } + + public FlowWork build() { + work.enable(); + return work; + } + + + } + + public class Relations { + + public Relations relation(String name, String source, String target) { + return relation(name,source,target,OutTrigger.defaultOutTrigger(),1,false); + } + + public Relations relation(String name, String source, String target, OutTrigger outTrigger,int order, boolean back) { + FlowNode from = work.getNodeByCode(source); + FlowNode to = work.getNodeByCode(target); + FlowRelation relation = new FlowRelation(RandomGenerator.generateUUID(), name, from, to, outTrigger,order, back); + work.addRelation(relation); + return this; + } + + public FlowWork build() { + work.enable(); + return work; + } + + + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/SchemaReader.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/SchemaReader.java new file mode 100644 index 00000000..bb5abd75 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/SchemaReader.java @@ -0,0 +1,92 @@ +package com.codingapi.springboot.flow.build; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowRelation; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.em.NodeType; +import com.codingapi.springboot.flow.error.ErrTrigger; +import com.codingapi.springboot.flow.generator.TitleGenerator; +import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.trigger.OutTrigger; +import lombok.Getter; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * 流程设计schema读取器 + */ +public class SchemaReader { + + private final JSONObject data; + + @Getter + private final List flowNodes; + @Getter + private final List flowRelations; + + public SchemaReader(String schema) { + this.data = JSONObject.parseObject(schema); + this.flowNodes = new ArrayList<>(); + this.flowRelations = new ArrayList<>(); + this.loadNodes(); + this.loadEdges(); + } + + + private void loadNodes(){ + JSONArray nodes = data.getJSONArray("nodes"); + for (int i = 0; i < nodes.size(); i++) { + JSONObject node = nodes.getJSONObject(i); + JSONObject properties = node.getJSONObject("properties"); + String code = properties.getString("code"); + String operatorMatcher = properties.getString("operatorMatcher"); + String titleGenerator = properties.getString("titleGenerator"); + String name = properties.getString("name"); + boolean editable = properties.getBoolean("editable"); + String view = properties.getString("view"); + String type = properties.getString("type"); + String approvalType = properties.getString("approvalType"); + int timeout = properties.getIntValue("timeout"); + String errTrigger = properties.getString("errTrigger"); + String id = properties.getString("id"); + FlowNode flowNode = new FlowNode(id,name,code,view, NodeType.parser(type),ApprovalType.parser(approvalType),new TitleGenerator(titleGenerator), + new OperatorMatcher(operatorMatcher),timeout, StringUtils.hasLength(errTrigger)?new ErrTrigger(errTrigger):null,editable); + flowNodes.add(flowNode); + } + } + + private FlowNode getFlowNodeById(String id){ + for(FlowNode flowNode:flowNodes){ + if(flowNode.getId().equals(id)){ + return flowNode; + } + } + return null; + } + + private void loadEdges(){ + JSONArray edges = data.getJSONArray("edges"); + for(int i=0;i historyRecords; + private final FlowSessionBeanProvider provider; + + public FlowSession(FlowWork flowWork, FlowNode flowNode, IFlowOperator createOperator, IFlowOperator currentOperator, IBindData bindData, Opinion opinion, List historyRecords) { + this.flowWork = flowWork; + this.flowNode = flowNode; + this.createOperator = createOperator; + this.currentOperator = currentOperator; + this.bindData = bindData; + this.opinion = opinion; + this.historyRecords = historyRecords; + this.provider = FlowSessionBeanProvider.getInstance(); + } + + + public Object getBean(String beanName) { + return provider.getBean(beanName); + } + + /** + * 创建节点结果 + * + * @param nodeCode 节点code + * @return 节点结果 + */ + public NodeResult createNodeErrTrigger(String nodeCode) { + return new NodeResult(nodeCode); + } + + /** + * 创建操作者结果 + * + * @param operatorIds 操作者id + * @return 操作者结果 + */ + public OperatorResult createOperatorErrTrigger(List operatorIds) { + return new OperatorResult(operatorIds); + } + + /** + * 创建操作者结果 + * + * @param operatorIds 操作者id + * @return 操作者结果 + */ + public OperatorResult createOperatorErrTrigger(long... operatorIds) { + return new OperatorResult(operatorIds); + } + + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSessionBeanProvider.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSessionBeanProvider.java new file mode 100644 index 00000000..58b9351d --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSessionBeanProvider.java @@ -0,0 +1,30 @@ +package com.codingapi.springboot.flow.content; + +import lombok.Getter; +import org.springframework.context.ApplicationContext; + +/** + * 流程回话 spring bean 提供者 + */ +public class FlowSessionBeanProvider { + + @Getter + private static final FlowSessionBeanProvider instance = new FlowSessionBeanProvider(); + + private FlowSessionBeanProvider() { + } + + private ApplicationContext spring; + + public void register(ApplicationContext spring) { + this.spring = spring; + } + + public Object getBean(String beanName) { + if (spring != null) { + return spring.getBean(beanName); + } + return null; + } + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java new file mode 100644 index 00000000..7d9291ed --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java @@ -0,0 +1,303 @@ +package com.codingapi.springboot.flow.domain; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.content.FlowSession; +import com.codingapi.springboot.flow.em.*; +import com.codingapi.springboot.flow.error.ErrTrigger; +import com.codingapi.springboot.flow.error.ErrorResult; +import com.codingapi.springboot.flow.generator.TitleGenerator; +import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.FlowOperatorRepository; +import com.codingapi.springboot.flow.serializable.FlowNodeSerializable; +import com.codingapi.springboot.flow.user.IFlowOperator; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.springframework.util.StringUtils; + +import java.util.List; + +/** + * 流程节点 + */ +@Getter +@AllArgsConstructor +public class FlowNode { + + public static final String CODE_START = "start"; + public static final String CODE_OVER = "over"; + + /** + * 节点id + */ + private String id; + + /** + * 节点编码 + */ + private String code; + + /** + * 节点名称 + */ + private String name; + + /** + * 节点标题创建规则 + */ + private TitleGenerator titleGenerator; + + /** + * 节点类型 | 分为发起、审批、结束 + */ + private NodeType type; + + /** + * 节点视图 + */ + private String view; + + /** + * 流程审批类型 | 分为会签、非会签 + */ + private ApprovalType approvalType; + + /** + * 操作者匹配器 + */ + private OperatorMatcher operatorMatcher; + + /** + * 是否可编辑 + */ + private boolean editable; + + /** + * 创建时间 + */ + private long createTime; + /** + * 更新时间 + */ + private long updateTime; + + /** + * 超时时间(毫秒) + */ + private long timeout; + + /** + * 异常触发器,当流程发生异常时异常通常是指找不到审批人,将会触发异常触发器,异常触发器可以是一个节点 + */ + @Setter + private ErrTrigger errTrigger; + + + public void verify(){ + if (this.titleGenerator == null) { + throw new IllegalArgumentException("titleGenerator is null"); + } + if (this.operatorMatcher == null) { + throw new IllegalArgumentException("operatorMatcher is null"); + } + if(timeout<0){ + throw new IllegalArgumentException("timeout is less than 0"); + } + if(!StringUtils.hasLength(id)){ + throw new IllegalArgumentException("id is empty"); + } + if(!StringUtils.hasLength(code)){ + throw new IllegalArgumentException("code is empty"); + } + } + + + /** + * 从序列化对象中创建节点 + * + * @return FlowNodeSerializable 序列号节点 + */ + public FlowNodeSerializable toSerializable() { + return new FlowNodeSerializable( + this.id, + this.code, + this.name, + this.titleGenerator.getScript(), + this.type, + this.view, + this.approvalType, + this.operatorMatcher.getScript(), + this.editable, + this.createTime, + this.updateTime, + this.timeout, + this.errTrigger == null ? null : this.errTrigger.getScript() + ); + } + + + public FlowNode(String id, + String name, + String code, + String view, + NodeType type, + ApprovalType approvalType, + TitleGenerator titleGenerator, + OperatorMatcher operatorMatcher, + long timeout, + ErrTrigger errTrigger, + boolean editable) { + this.id = id; + this.code = code; + this.name = name; + this.titleGenerator = titleGenerator; + this.type = type; + this.view = view; + this.approvalType = approvalType; + this.operatorMatcher = operatorMatcher; + this.createTime = System.currentTimeMillis(); + this.updateTime = System.currentTimeMillis(); + this.errTrigger = errTrigger; + this.timeout = timeout; + this.editable = editable; + } + + + /** + * 加载节点的操作者 + * + * @param flowSession 操作内容 + * @return 是否匹配 + */ + public List loadFlowNodeOperator(FlowSession flowSession, FlowOperatorRepository flowOperatorRepository) { + return flowOperatorRepository.findByIds(this.operatorMatcher.matcher(flowSession)); + } + + + /** + * 创建流程记录 + * + * @param workId 流程设计id + * @param processId 流程id + * @param preId 上一条流程记录id + * @param title 流程标题 + * @param createOperator 流程操作者 + * @param currentOperator 当前操作者 + * @param snapshot 快照数据 + * @return 流程记录 + */ + public FlowRecord createRecord(long workId, + String processId, + long preId, + String title, + IFlowOperator createOperator, + IFlowOperator currentOperator, + BindDataSnapshot snapshot + ) { + + // 当前操作者存在委托人时,才需要寻找委托人 + IFlowOperator flowOperator = currentOperator; + while (flowOperator.entrustOperator() != null) { + //寻找委托人 + flowOperator = flowOperator.entrustOperator(); + } + FlowRecord record = new FlowRecord(); + record.setProcessId(processId); + record.setNodeCode(this.code); + record.setCreateTime(System.currentTimeMillis()); + record.setWorkId(workId); + record.setFlowStatus(FlowStatus.RUNNING); + record.setPostponedCount(0); + record.setCreateOperator(createOperator); + record.setBindClass(snapshot.getClazzName()); + record.setCurrentOperator(flowOperator); + record.setPreId(preId); + record.setTitle(title); + record.setTimeoutTime(this.loadTimeoutTime()); + record.setFlowType(FlowType.TODO); + record.setErrMessage(null); + record.setSnapshotId(snapshot.getId()); + return record; + } + + + /** + * 获取超时时间 + * + * @return 超时时间 + */ + private long loadTimeoutTime() { + if (this.timeout > 0) { + return System.currentTimeMillis() + this.timeout; + } + return 0; + } + + /** + * 是否有任意操作者匹配 + */ + public boolean isAnyOperatorMatcher() { + return operatorMatcher.isAny(); + } + + /** + * 异常匹配 + * + * @param flowSession 操作内容 + */ + public ErrorResult errMatcher(FlowSession flowSession) { + if (errTrigger != null) { + return errTrigger.trigger(flowSession); + } + return null; + } + + /** + * 是否有异常触发器 + * + * @return 是否有异常触发器 + */ + public boolean hasErrTrigger() { + return errTrigger != null; + } + + /** + * 生成标题 + * + * @param flowSession 流程内容 + * @return 标题 + */ + public String generateTitle(FlowSession flowSession) { + return titleGenerator.generate(flowSession); + } + + + /** + * 是否会签节点 + */ + public boolean isSign() { + return approvalType == ApprovalType.SIGN; + } + + /** + * 是否非会签节点 + */ + public boolean isUnSign() { + return approvalType == ApprovalType.UN_SIGN; + } + + /** + * 是否结束节点 + */ + public boolean isOverNode() { + return CODE_OVER.equals(this.code); + } + + /** + * 是否开始节点 + */ + public boolean isStartNode() { + return CODE_START.equals(this.code); + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowRelation.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowRelation.java new file mode 100644 index 00000000..c93dead9 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowRelation.java @@ -0,0 +1,168 @@ +package com.codingapi.springboot.flow.domain; + +import com.codingapi.springboot.flow.content.FlowSession; +import com.codingapi.springboot.flow.em.NodeType; +import com.codingapi.springboot.flow.serializable.FlowRelationSerializable; +import com.codingapi.springboot.flow.trigger.OutTrigger; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.util.StringUtils; + +import java.util.List; + +/** + * 流程关系 + */ +@Getter +@AllArgsConstructor +public class FlowRelation { + + /** + * 关系id + */ + private String id; + + /** + * 名称 + */ + private String name; + + /** + * 源节点 + */ + private FlowNode source; + + /** + * 目标节点 + */ + private FlowNode target; + + /** + * 排序 + */ + private int order; + + /** + * 是否退回 + */ + private boolean back; + + /** + * 出口触发器 + */ + private OutTrigger outTrigger; + + + /** + * 创建时间 + */ + private long createTime; + + /** + * 修改时间 + */ + private long updateTime; + + + /** + * 序列化 + * + * @return 序列化对象 + */ + public FlowRelationSerializable toSerializable() { + return new FlowRelationSerializable(id, + name, + source.getId(), + target.getId(), + order, + back, + outTrigger.getScript(), + createTime, + updateTime + ); + } + + /** + * 匹配节点 + * + * @param nodeCode 节点编码 + * @return 是否匹配 + */ + public boolean sourceMatcher(String nodeCode) { + return source.getCode().equals(nodeCode); + } + + + /** + * 重新排序 + * + * @param order 排序 + */ + public void resort(int order) { + this.order = order; + } + + + public FlowRelation(String id, String name, FlowNode source, FlowNode target, OutTrigger outTrigger, int order, boolean back) { + this.id = id; + this.name = name; + this.source = source; + this.target = target; + this.outTrigger = outTrigger; + this.order = order; + this.back = back; + this.createTime = System.currentTimeMillis(); + this.updateTime = System.currentTimeMillis(); + } + + /** + * 触发条件 + * + * @param flowSession 流程内容 + * @return 下一个节点 + */ + public FlowNode trigger(FlowSession flowSession) { + if (outTrigger.trigger(flowSession)) { + return target; + } + return null; + } + + + /** + * 验证 + */ + public void verify() { + if (!StringUtils.hasLength(id)) { + throw new RuntimeException("id is null"); + } + + if (source == null || target == null) { + throw new RuntimeException("source or target is null"); + } + + if (outTrigger == null) { + throw new RuntimeException("outTrigger is null"); + } + + if(source.getCode().equals(target.getCode())){ + throw new RuntimeException("source node code is equals target node code"); + } + + if(back){ + if(source.getType() != NodeType.APPROVAL){ + throw new RuntimeException("source node type is not approval"); + } + } + } + + public void verifyNodes(List nodes) { + if (nodes.stream().noneMatch(node -> node.getId().equals(source.getId()))) { + throw new RuntimeException("source node is not exists"); + } + + if (nodes.stream().noneMatch(node -> node.getId().equals(target.getId()))) { + throw new RuntimeException("target node is not exists"); + } + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowWork.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowWork.java new file mode 100644 index 00000000..377aedd2 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowWork.java @@ -0,0 +1,332 @@ +package com.codingapi.springboot.flow.domain; + +import com.codingapi.springboot.flow.build.SchemaReader; +import com.codingapi.springboot.flow.serializable.FlowWorkSerializable; +import com.codingapi.springboot.flow.user.IFlowOperator; +import com.codingapi.springboot.framework.utils.RandomGenerator; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 流程设计 + */ +@Getter +@AllArgsConstructor +public class FlowWork { + + /** + * 流程的设计id + */ + @Setter + private long id; + + /** + * 流程编码 (唯一值) + */ + @Setter + private String code; + + /** + * 流程标题 + */ + @Setter + private String title; + /** + * 流程描述 + */ + @Setter + private String description; + /** + * 流程创建者 + */ + private IFlowOperator createUser; + /** + * 创建时间 + */ + private long createTime; + /** + * 更新时间 + * 也是流程的版本号 + */ + private long updateTime; + /** + * 是否启用 + */ + private boolean enable; + + /** + * 最大延期次数 + */ + @Setter + private int postponedMax; + + /** + * 流程的节点(发起节点) + */ + private List nodes; + + /** + * 流程的关系 + */ + private List relations; + + /** + * 界面设计脚本 + */ + private String schema; + + /** + * 构造函数 + * + * @param createUser 创建者 + */ + public FlowWork(IFlowOperator createUser) { + this.createUser = createUser; + this.createTime = System.currentTimeMillis(); + this.updateTime = System.currentTimeMillis(); + this.nodes = new ArrayList<>(); + this.relations = new ArrayList<>(); + this.enable = false; + this.postponedMax = 1; + this.code = RandomGenerator.randomString(8); + } + + + /** + * 流程设计复制 + * @return FlowWork 流程设计 + */ + public FlowWork copy(){ + if(!StringUtils.hasLength(schema)){ + throw new IllegalArgumentException("schema is empty"); + } + String schema = this.getSchema(); + for(FlowNode flowNode:this.getNodes()){ + String newId = RandomGenerator.generateUUID(); + schema = schema.replaceAll(flowNode.getId(),newId); + } + + for(FlowRelation relation:this.getRelations()){ + String newId = RandomGenerator.generateUUID(); + schema = schema.replaceAll(relation.getId(),newId); + } + + FlowWork flowWork = new FlowWork(this.createUser); + flowWork.setDescription(this.getDescription()); + flowWork.setTitle(this.getTitle()); + flowWork.setCode(RandomGenerator.randomString(8)); + flowWork.setPostponedMax(this.getPostponedMax()); + flowWork.schema(schema); + return flowWork; + } + + + public FlowWork(String code,String title, String description, int postponedMax, IFlowOperator createUser) { + this.title = title; + this.code = code; + this.description = description; + this.postponedMax = postponedMax; + this.createUser = createUser; + this.createTime = System.currentTimeMillis(); + this.updateTime = System.currentTimeMillis(); + this.nodes = new ArrayList<>(); + this.relations = new ArrayList<>(); + this.enable = false; + } + + + public void verify() { + if (this.nodes == null || this.nodes.isEmpty()) { + throw new IllegalArgumentException("nodes is empty"); + } + if (this.relations == null || this.relations.isEmpty()) { + throw new IllegalArgumentException("relations is empty"); + } + if (!StringUtils.hasLength(title)) { + throw new IllegalArgumentException("title is empty"); + } + if (!StringUtils.hasLength(code)) { + throw new IllegalArgumentException("code is empty"); + } + + this.verifyNodes(); + this.verifyRelations(); + this.checkRelation(); + } + + + private void checkRelation() { + FlowNode startNode = getNodeByCode(FlowNode.CODE_START); + if (startNode == null) { + throw new IllegalArgumentException("start node is not exist"); + } + FlowNode overNode = getNodeByCode(FlowNode.CODE_OVER); + if (overNode == null) { + throw new IllegalArgumentException("over node is not exist"); + } + + List sourceCodes = new ArrayList<>(); + List targetCodes = new ArrayList<>(); + for (FlowRelation relation : relations) { + sourceCodes.add(relation.getSource().getCode()); + targetCodes.add(relation.getTarget().getCode()); + } + + if (!sourceCodes.contains(FlowNode.CODE_START)) { + throw new IllegalArgumentException("start node relation is not exist"); + } + + if (!targetCodes.contains(FlowNode.CODE_OVER)) { + throw new IllegalArgumentException("over node relation is not exist"); + } + + } + + + private void verifyNodes() { + List nodeCodes = new ArrayList<>(); + + for (FlowNode node : nodes) { + node.verify(); + if (nodeCodes.contains(node.getCode())) { + throw new IllegalArgumentException("node code is exist"); + } + nodeCodes.add(node.getCode()); + } + } + + + private void verifyRelations() { + for (FlowRelation relation : relations) { + relation.verify(); + + relation.verifyNodes(nodes); + } + } + + + /** + * 序列化 + * + * @return FlowSerializable 流程序列化对象 + */ + public FlowWorkSerializable toSerializable() { + return new FlowWorkSerializable( + id, + code, + title, + description, + createUser.getUserId(), + createTime, + updateTime, + enable, + postponedMax, + nodes.stream().map(FlowNode::toSerializable).collect(Collectors.toCollection(ArrayList::new)), + relations.stream().map(FlowRelation::toSerializable).collect(Collectors.toCollection(ArrayList::new))); + } + + + /** + * schema解析流程设计 + * + * @param schema schema + */ + public void schema(String schema) { + SchemaReader schemaReader = new SchemaReader(schema); + this.relations = schemaReader.getFlowRelations(); + this.nodes = schemaReader.getFlowNodes(); + this.schema = schema; + this.verify(); + this.updateTime = System.currentTimeMillis(); + } + + /** + * 添加节点 + * + * @param node 节点 + */ + public void addNode(FlowNode node) { + List codes = nodes.stream().map(FlowNode::getCode).collect(Collectors.toList()); + if (codes.contains(node.getCode())) { + throw new IllegalArgumentException("node code is exist"); + } + nodes.add(node); + this.updateTime = System.currentTimeMillis(); + } + + /** + * 添加关系 + * + * @param relation 关系 + */ + public void addRelation(FlowRelation relation) { + relations.add(relation); + this.updateTime = System.currentTimeMillis(); + } + + + /** + * 获取节点 + * + * @param code 节点编码 + * @return 节点 + */ + public FlowNode getNodeByCode(String code) { + for (FlowNode node : nodes) { + if (node.getCode().equals(code)) { + return node; + } + } + return null; + } + + + /** + * 获取开始节点 + * + * @return 开始节点 + */ + public FlowNode getStartNode() { + return getNodeByCode(FlowNode.CODE_START); + } + + + /** + * 是否存在退回关系 + */ + public boolean hasBackRelation() { + return relations.stream().anyMatch(FlowRelation::isBack); + } + + + /** + * 启用检测 + */ + public void enableValidate() { + if (!this.isEnable()) { + throw new IllegalArgumentException("flow work is disable"); + } + } + + + /** + * 启用 + */ + public void enable() { + this.verify(); + this.enable = true; + } + + /** + * 禁用 + */ + public void disbale() { + this.enable = false; + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/Opinion.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/Opinion.java new file mode 100644 index 00000000..3f3321ae --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/Opinion.java @@ -0,0 +1,78 @@ +package com.codingapi.springboot.flow.domain; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * 审批意见 + */ +@Getter +@Setter +@ToString +@NoArgsConstructor +public class Opinion { + + // 默认审批(人工审批) + public static final int TYPE_DEFAULT = 0; + // 系统自动审批 + public static final int TYPE_AUTO = 1; + + + // 审批结果 暂存 + public static final int RESULT_SAVE = 0; + // 审批结果 转办 + public static final int RESULT_TRANSFER = 1; + // 审批结果 通过 + public static final int RESULT_PASS = 2; + // 审批结果 驳回 + public static final int RESULT_REJECT = 3; + + /** + * 审批意见 + */ + private String advice; + /** + * 审批结果 + */ + private int result; + /** + * 意见类型 + */ + private int type; + + public Opinion(String advice, int result, int type) { + this.advice = advice; + this.result = result; + this.type = type; + } + + public static Opinion save(String advice) { + return new Opinion(advice, RESULT_SAVE, TYPE_DEFAULT); + } + + public static Opinion pass(String advice) { + return new Opinion(advice, RESULT_PASS, TYPE_DEFAULT); + } + + public static Opinion reject(String advice) { + return new Opinion(advice, RESULT_REJECT, TYPE_DEFAULT); + } + + public static Opinion transfer(String advice) { + return new Opinion(advice, RESULT_TRANSFER, TYPE_DEFAULT); + } + + public static Opinion unSignAutoSuccess() { + return new Opinion("非会签自动审批", RESULT_PASS, TYPE_AUTO); + } + + public boolean isSuccess() { + return result == RESULT_PASS; + } + + public boolean isReject() { + return result == RESULT_REJECT; + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/ApprovalType.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/ApprovalType.java new file mode 100644 index 00000000..60747587 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/ApprovalType.java @@ -0,0 +1,26 @@ +package com.codingapi.springboot.flow.em; + +/** + * 审批类型:会签与非会签 + */ +public enum ApprovalType { + + /** + * 会签 + */ + SIGN, + /** + * 非会签 + */ + UN_SIGN; + + + public static ApprovalType parser(String approvalType) { + for(ApprovalType type:values()){ + if(type.name().equalsIgnoreCase(approvalType)){ + return type; + } + } + return UN_SIGN; + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowSourceDirection.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowSourceDirection.java new file mode 100644 index 00000000..deb82606 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowSourceDirection.java @@ -0,0 +1,30 @@ +package com.codingapi.springboot.flow.em; + +/** + * 流程来源的方式 + * 包括 同意、拒绝、转办 + */ +public enum FlowSourceDirection { + + /** + * 同意 + */ + PASS, + /** + * 拒绝 + */ + REJECT, + /** + * + */ + TRANSFER; + + public static FlowSourceDirection parser(String type){ + for(FlowSourceDirection flowSourceDirection :values()){ + if(flowSourceDirection.name().equalsIgnoreCase(type)){ + return flowSourceDirection; + } + } + return null; + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowStatus.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowStatus.java new file mode 100644 index 00000000..7c4bceec --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowStatus.java @@ -0,0 +1,27 @@ +package com.codingapi.springboot.flow.em; + +/** + * 流程状态 + * 进行中、已完成 + */ +public enum FlowStatus { + + /** + * 进行中 + */ + RUNNING, + /** + * 已完成 + */ + FINISH; + + + public static FlowStatus parser(String status) { + for (FlowStatus flowStatus : FlowStatus.values()) { + if (flowStatus.name().equalsIgnoreCase(status)) { + return flowStatus; + } + } + return RUNNING; + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowType.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowType.java new file mode 100644 index 00000000..abee3ff0 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowType.java @@ -0,0 +1,30 @@ +package com.codingapi.springboot.flow.em; + +/** + * 流程的类型 + */ +public enum FlowType { + + /** + * 待办 + */ + TODO, + /** + * 已办 + */ + DONE, + /** + * 转办 + */ + TRANSFER; + + + public static FlowType parser(String type){ + for(FlowType flowType :values()){ + if(flowType.name().equalsIgnoreCase(type)){ + return flowType; + } + } + return TODO; + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/NodeType.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/NodeType.java new file mode 100644 index 00000000..ac87e905 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/NodeType.java @@ -0,0 +1,30 @@ +package com.codingapi.springboot.flow.em; + +/** + * 节点类型 + */ +public enum NodeType { + + /** + * 发起 + */ + START, + /** + * 审批 + */ + APPROVAL, + /** + * 结束 + */ + OVER; + + + public static NodeType parser(String type) { + for (NodeType nodeType : values()) { + if (nodeType.name().equalsIgnoreCase(type)) { + return nodeType; + } + } + return APPROVAL; + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/error/ErrTrigger.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/error/ErrTrigger.java new file mode 100644 index 00000000..fbdd6751 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/error/ErrTrigger.java @@ -0,0 +1,36 @@ +package com.codingapi.springboot.flow.error; + +import com.codingapi.springboot.flow.content.FlowSession; +import com.codingapi.springboot.flow.script.GroovyShellContext; +import lombok.Getter; +import org.springframework.util.StringUtils; + +/** + * 错误触发器 + */ +public class ErrTrigger { + + @Getter + private final String script; + + private final GroovyShellContext.ShellScript runtime; + + + public ErrTrigger(String script) { + if (!StringUtils.hasLength(script)) { + throw new IllegalArgumentException("script is empty"); + } + this.script = script; + this.runtime = GroovyShellContext.getInstance().parse(script); + } + + /** + * 触发 + * + * @param flowSession 流程内容 + */ + public ErrorResult trigger(FlowSession flowSession) { + return (ErrorResult) runtime.invokeMethod("run", flowSession); + } + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/error/ErrorResult.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/error/ErrorResult.java new file mode 100644 index 00000000..96d8b272 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/error/ErrorResult.java @@ -0,0 +1,17 @@ +package com.codingapi.springboot.flow.error; + + +/** + * 异常匹配的结果对象 + */ +public abstract class ErrorResult { + + public boolean isNode(){ + return this instanceof NodeResult; + } + + public boolean isOperator(){ + return this instanceof OperatorResult; + } + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/error/NodeResult.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/error/NodeResult.java new file mode 100644 index 00000000..ebcf5fd0 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/error/NodeResult.java @@ -0,0 +1,16 @@ +package com.codingapi.springboot.flow.error; + +import lombok.Getter; + +/** + * 节点的异常匹配对象 + */ +@Getter +public class NodeResult extends ErrorResult{ + + private final String node; + + public NodeResult(String node) { + this.node = node; + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/error/OperatorResult.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/error/OperatorResult.java new file mode 100644 index 00000000..9467626a --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/error/OperatorResult.java @@ -0,0 +1,25 @@ +package com.codingapi.springboot.flow.error; + +import lombok.Getter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 操作人员的异常匹配对象 + */ +@Getter +public class OperatorResult extends ErrorResult { + + private final List operatorIds; + + public OperatorResult(List operatorIds) { + this.operatorIds = operatorIds; + } + + public OperatorResult(long... operatorIds) { + this.operatorIds = new ArrayList<>(); + Arrays.stream(operatorIds).forEach(this.operatorIds::add); + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/event/FlowApprovalEvent.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/event/FlowApprovalEvent.java new file mode 100644 index 00000000..f005767e --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/event/FlowApprovalEvent.java @@ -0,0 +1,88 @@ +package com.codingapi.springboot.flow.event; + +import com.codingapi.springboot.flow.bind.IBindData; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.user.IFlowOperator; +import com.codingapi.springboot.framework.event.ISyncEvent; +import lombok.Getter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + +/** + * 流程审批事件 + */ +@Slf4j +@Getter +@ToString +public class FlowApprovalEvent implements ISyncEvent { + // 创建流程 + public static final int STATE_CREATE = 1; + // 流程审批通过 + public static final int STATE_PASS = 2; + // 流程审批拒绝 + public static final int STATE_REJECT = 3; + // 流程转办 + public static final int STATE_TRANSFER = 4; + // 流程撤销 + public static final int STATE_RECALL = 5; + // 流程完成 + public static final int STATE_FINISH = 6; + // 创建待办 + public static final int STATE_TODO = 7; + // 催办 + public static final int STATE_URGE = 8; + + + private final int state; + private final IFlowOperator operator; + private final FlowRecord flowRecord; + private final FlowWork flowWork; + private final IBindData bindData; + + public FlowApprovalEvent(int state, FlowRecord flowRecord, IFlowOperator operator, FlowWork flowWork, IBindData bindData) { + this.state = state; + this.operator = operator; + this.flowRecord = flowRecord; + this.flowWork = flowWork; + this.bindData = bindData; + log.debug("FlowApprovalEvent:{}", this); + } + + + public boolean match(Class bindDataClass) { + return bindDataClass.isInstance(bindData); + } + + public boolean isUrge() { + return state == STATE_URGE; + } + + public boolean isTodo() { + return state == STATE_TODO; + } + + public boolean isCreate() { + return state == STATE_CREATE; + } + + public boolean isPass() { + return state == STATE_PASS; + } + + public boolean isReject() { + return state == STATE_REJECT; + } + + public boolean isTransfer() { + return state == STATE_TRANSFER; + } + + public boolean isRecall() { + return state == STATE_RECALL; + } + + public boolean isFinish() { + return state == STATE_FINISH; + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/generator/TitleGenerator.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/generator/TitleGenerator.java new file mode 100644 index 00000000..2fa5f840 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/generator/TitleGenerator.java @@ -0,0 +1,47 @@ +package com.codingapi.springboot.flow.generator; + +import com.codingapi.springboot.flow.content.FlowSession; +import com.codingapi.springboot.flow.script.GroovyShellContext; +import lombok.Getter; +import org.springframework.util.StringUtils; + +/** + * 标题生成器 + */ +public class TitleGenerator { + + @Getter + private final String script; + + private final GroovyShellContext.ShellScript runtime; + + public TitleGenerator(String script) { + if (!StringUtils.hasLength(script)) { + throw new IllegalArgumentException("script is empty"); + } + this.script = script; + this.runtime = GroovyShellContext.getInstance().parse(script); + } + + + /** + * 默认标题生成器 + * + * @return 标题生成器 + */ + public static TitleGenerator defaultTitleGenerator() { + return new TitleGenerator("def run(content){ return content.getCurrentOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}"); + } + + + /** + * 生成标题 + * + * @param flowSession 流程内容 + * @return 标题 + */ + public String generate(FlowSession flowSession) { + return (String) this.runtime.invokeMethod("run", flowSession); + } + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/matcher/OperatorMatcher.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/matcher/OperatorMatcher.java new file mode 100644 index 00000000..ae252f0b --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/matcher/OperatorMatcher.java @@ -0,0 +1,115 @@ +package com.codingapi.springboot.flow.matcher; + +import com.codingapi.springboot.flow.content.FlowSession; +import com.codingapi.springboot.flow.script.GroovyShellContext; +import lombok.Getter; +import org.springframework.util.StringUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 操作者匹配器 + */ +public class OperatorMatcher { + + @Getter + private final String script; + + private final int state; + + private final GroovyShellContext.ShellScript runtime; + + // 指定用户 + public static final int STATE_SPECIFY = 1; + // 创建者 + public static final int STATE_CREATOR = 2; + // 任意人 + public static final int STATE_ANY = 3; + + + public boolean isAny() { + return state == STATE_ANY; + } + + public boolean isCreator() { + return state == STATE_CREATOR; + } + + public boolean isSpecify() { + return state == STATE_SPECIFY; + } + + private static int parseState(String script) { + if (script.contains("content.getCurrentOperator().getUserId()")) { + return STATE_ANY; + } else if (script.contains("content.getCreateOperator().getUserId()")) { + return STATE_CREATOR; + } else { + return STATE_SPECIFY; + } + } + + + public OperatorMatcher(String script) { + this(script, parseState(script)); + } + + public OperatorMatcher(String script, int state) { + if (!StringUtils.hasLength(script)) { + throw new IllegalArgumentException("script is empty"); + } + this.script = script; + this.state = state; + this.runtime = GroovyShellContext.getInstance().parse(script); + } + + /** + * 默认操作者匹配器 + * + * @return 操作者匹配器 + */ + public static OperatorMatcher anyOperatorMatcher() { + return new OperatorMatcher("def run(content) {return [content.getCurrentOperator().getUserId()];}", STATE_ANY); + } + + /** + * 指定操作者匹配器 + * + * @param userIds 用户ids + * @return 操作者匹配器 + */ + public static OperatorMatcher specifyOperatorMatcher(long... userIds) { + String userIdsStr = Arrays.stream(userIds).mapToObj(String::valueOf).collect(Collectors.joining(",")); + return new OperatorMatcher("def run(content) {return [" + userIdsStr + "];}", STATE_SPECIFY); + } + + /** + * 创建者操作者匹配器 + * + * @param userIds 用户ids + * @return 操作者匹配器 + */ + public static OperatorMatcher creatorOperatorMatcher() { + return new OperatorMatcher("def run(content) {return [content.getCreateOperator().getUserId()];}", STATE_CREATOR); + } + + /** + * 匹配操作者 + * + * @param flowSession 流程内容 + * @return 是否匹配 + */ + public List matcher(FlowSession flowSession) { + List values = (List) runtime.invokeMethod("run", flowSession); + return values.stream().map(item -> { + if (item instanceof Number) { + return ((Number) item).longValue(); + } else { + return Long.parseLong(item.toString()); + } + }).collect(Collectors.toList()); + } + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java new file mode 100644 index 00000000..f889f59f --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java @@ -0,0 +1,95 @@ +package com.codingapi.springboot.flow.pojo; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.bind.IBindData; +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.user.IFlowOperator; +import lombok.Getter; + +import java.util.List; +import java.util.stream.Collectors; + + +/** + * 流程详情的阻止对象 + */ +@Getter +public class FlowDetail { + + /** + * 当前流程 + */ + private final FlowRecord flowRecord; + /** + * 流程设计器 + */ + private final FlowWork flowWork; + /** + * 流程节点 + */ + private final FlowNode flowNode; + /** + * 历史流程 + */ + private final List historyRecords; + /** + * 绑定数据 + */ + private final IBindData bindData; + /** + * 全部的操作人 + */ + private final List operators; + + /** + * 创建者 + */ + private final IFlowOperator flowCreator; + + /** + * 创建时间 + */ + private final long flowCreateTime; + + /** + * 流程的意见 + */ + private final List opinions; + + + public FlowDetail(FlowRecord flowRecord, + BindDataSnapshot snapshot, + FlowWork flowWork, + List historyRecords, + List operators) { + this.operators = operators; + this.flowRecord = flowRecord; + this.flowWork = flowWork; + this.bindData = snapshot.toBindData(); + this.historyRecords = historyRecords; + this.opinions = historyRecords.stream().map(FlowOpinion::new).collect(Collectors.toList()); + this.flowCreator = flowRecord.getCreateOperator(); + this.flowCreateTime = flowRecord.getCreateTime(); + this.flowNode = flowWork.getNodeByCode(flowRecord.getNodeCode()); + } + + + @Getter + public final class FlowOpinion { + private final long recordId; + private final Opinion opinion; + private final IFlowOperator operator; + private final long createTime; + + public FlowOpinion(FlowRecord flowRecord) { + this.recordId = flowRecord.getId(); + this.opinion = flowRecord.getOpinion(); + this.operator = flowRecord.getCurrentOperator(); + this.createTime = flowRecord.getUpdateTime(); + } + } + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowResult.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowResult.java new file mode 100644 index 00000000..4e16e495 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowResult.java @@ -0,0 +1,26 @@ +package com.codingapi.springboot.flow.pojo; + +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.record.FlowRecord; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +public class FlowResult { + + private final List records; + private final FlowWork flowWork; + + public FlowResult(FlowWork flowWork, List records) { + this.flowWork = flowWork; + this.records = records; + } + + public FlowResult(FlowWork flowWork,FlowRecord flowRecord) { + this.flowWork = flowWork; + this.records = new ArrayList<>(); + this.records.add(flowRecord); + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/query/FlowRecordQuery.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/query/FlowRecordQuery.java new file mode 100644 index 00000000..de2e4298 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/query/FlowRecordQuery.java @@ -0,0 +1,55 @@ +package com.codingapi.springboot.flow.query; + +import com.codingapi.springboot.flow.record.FlowRecord; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; + +/** + * 流程记录查询服务 + */ +public interface FlowRecordQuery { + + + Page findAll(PageRequest pageRequest); + + /** + * 查看个人的待办数据 + * + * @param operatorId 操作人 + * @return 流程记录 + */ + Page findTodoByOperatorId(long operatorId, PageRequest pageRequest); + + + /** + * 查看个人的已办数据 + * @param operatorId 操作人 + * @return 流程记录 + */ + Page findDoneByOperatorId(long operatorId, PageRequest pageRequest); + + + /** + * 查看个人的发起数据 (含待办与已办) + * @param operatorId 操作人 + * @return 流程记录 + */ + Page findInitiatedByOperatorId(long operatorId, PageRequest pageRequest); + + + /** + * 查看个人的超时的待办流程 + * @param operatorId 操作人 + * @return 流程记录 + */ + Page findTimeoutTodoByOperatorId(long operatorId, PageRequest pageRequest); + + + /** + * 查看个人的延期的待办流程 + * @param operatorId 操作人 + * @return 流程记录 + */ + Page findPostponedTodoByOperatorId(long operatorId, PageRequest pageRequest); + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowBackup.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowBackup.java new file mode 100644 index 00000000..1203e142 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowBackup.java @@ -0,0 +1,61 @@ +package com.codingapi.springboot.flow.record; + +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.repository.FlowOperatorRepository; +import com.codingapi.springboot.flow.serializable.FlowWorkSerializable; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * 流程备份 + */ +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class FlowBackup { + + /** + * 备份id + */ + private long id; + + /** + * 流程的字节码 + */ + private byte[] bytes; + + /** + * 创建时间 + */ + private long createTime; + + /** + * 流程的版本号 + * 以流程的修改时间为准 + */ + private long workVersion; + + /** + * 流程的设计id + */ + private long workId; + + /** + * 恢复流程 + * @param flowOperatorRepository 操作者仓库 + * @return 流程 + */ + public FlowWork resume(FlowOperatorRepository flowOperatorRepository) { + return FlowWorkSerializable.fromSerializable(this.bytes).toFlowWork(flowOperatorRepository); + } + + public FlowBackup(FlowWork flowWork) { + this.bytes = flowWork.toSerializable().toSerializable(); + this.workVersion = flowWork.getUpdateTime(); + this.workId = flowWork.getId(); + this.createTime = System.currentTimeMillis(); + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowProcess.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowProcess.java new file mode 100644 index 00000000..b8a4f975 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowProcess.java @@ -0,0 +1,42 @@ +package com.codingapi.springboot.flow.record; + +import com.codingapi.springboot.flow.user.IFlowOperator; +import com.codingapi.springboot.framework.utils.RandomGenerator; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程process记录 + */ +@Getter +@AllArgsConstructor +public class FlowProcess { + + /** + * 流程id + */ + private String processId; + + /** + * 创建时间 + */ + private long createTime; + + /** + * 流程的字节码 + */ + private long backupId; + + /** + * 创建者id + */ + private long createOperatorId; + + + public FlowProcess(long backupId, IFlowOperator createOperator) { + this.processId = RandomGenerator.generateUUID(); + this.createTime = System.currentTimeMillis(); + this.backupId = backupId; + this.createOperatorId = createOperator.getUserId(); + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java new file mode 100644 index 00000000..41a57d1b --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java @@ -0,0 +1,417 @@ +package com.codingapi.springboot.flow.record; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.FlowSourceDirection; +import com.codingapi.springboot.flow.em.FlowStatus; +import com.codingapi.springboot.flow.em.FlowType; +import com.codingapi.springboot.flow.user.IFlowOperator; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * 流程记录 + */ +@Setter +@Getter +@ToString +public class FlowRecord { + + /** + * 流程记录id + */ + private long id; + + /** + * 上一个流程记录id + */ + private long preId; + + /** + * 工作id + */ + private long workId; + /** + * 流程id + */ + private String processId; + + /** + * 节点 + */ + private String nodeCode; + + /** + * 流程标题 + */ + private String title; + /** + * 当前操作者 + */ + private IFlowOperator currentOperator; + /** + * 节点状态 | 待办、已办 + */ + private FlowType flowType; + + /** + * 流转产生方式 + * 流程是退回产生的还是通过产生的 + */ + private FlowSourceDirection flowSourceDirection; + + /** + * 创建时间 + */ + private long createTime; + + /** + * 更新时间 + */ + private long updateTime; + + /** + * 完成时间 + */ + private long finishTime; + + /** + * 超时到期时间 + */ + private long timeoutTime; + + /** + * 延期次数 + */ + private int postponedCount; + + /** + * 发起者id + */ + private IFlowOperator createOperator; + /** + * 审批意见 (为办理时为空) + */ + private Opinion opinion; + /** + * 流程状态 | 进行中、已完成 + */ + private FlowStatus flowStatus; + /** + * 异常信息 + */ + private String errMessage; + + /** + * 绑定数据的类 + */ + private String bindClass; + + /** + * 绑定数据的快照 + */ + private long snapshotId; + + /** + * 是否已读 + */ + private boolean read; + + /** + * 是否干预 + */ + private boolean interfere; + + /** + * 被干预的用户 + */ + private IFlowOperator interferedOperator; + + /** + * 已读时间 + */ + private long readTime; + + /** + * 延期时间 + * + * @param postponedMax 最大延期次数 + * @param time 延期时间(毫秒) + */ + public void postponedTime(int postponedMax, long time) { + if (this.postponedCount >= postponedMax) { + throw new IllegalArgumentException("postponed count is max"); + } + this.read(); + if (this.timeoutTime == 0) { + this.timeoutTime = System.currentTimeMillis(); + } + this.timeoutTime += time; + this.postponedCount++; + this.updateTime = System.currentTimeMillis(); + } + + /** + * 是否是发起节点 + */ + public boolean isInitiated() { + return preId == 0 && this.nodeCode.equals(FlowNode.CODE_START); + } + + /** + * 已读 + */ + public void read() { + this.read = true; + this.readTime = System.currentTimeMillis(); + } + + /** + * 是否未读 + */ + public boolean isUnRead() { + return !this.read; + } + + + /** + * 更新opinion + */ + public void updateOpinion(Opinion opinion) { + this.opinion = opinion; + this.updateTime = System.currentTimeMillis(); + } + + /** + * 提交状态校验 + * 是否可以提交 + */ + public void submitStateVerify() { + if (flowStatus == FlowStatus.FINISH) { + throw new IllegalArgumentException("flow is finish"); + } + if (flowType == FlowType.DONE) { + throw new IllegalArgumentException("flow is done"); + } + } + + /** + * 提交流程 + * + * @param flowOperator 操作者 + * @param snapshot 绑定数据 + * @param opinion 意见 + * @param flowSourceDirection 流转方式 + */ + public void submitRecord(IFlowOperator flowOperator, BindDataSnapshot snapshot, Opinion opinion, FlowSourceDirection flowSourceDirection) { + if (!flowOperator.isFlowManager()) { + if (flowOperator.getUserId() != this.currentOperator.getUserId()) { + throw new IllegalArgumentException("current operator is not match"); + } + } else { + this.interferedOperator = this.currentOperator; + this.currentOperator = flowOperator; + this.interfere = true; + } + this.read(); + this.flowSourceDirection = flowSourceDirection; + this.flowType = FlowType.DONE; + this.updateTime = System.currentTimeMillis(); + this.snapshotId = snapshot.getId(); + this.bindClass = snapshot.getClazzName(); + this.opinion = opinion; + } + + /** + * 转交流程 + */ + public void transfer(IFlowOperator flowOperator, BindDataSnapshot snapshot, Opinion opinion) { + if (flowOperator.getUserId() != this.currentOperator.getUserId()) { + throw new IllegalArgumentException("current operator is not match"); + } + this.read(); + this.flowSourceDirection = FlowSourceDirection.TRANSFER; + this.flowType = FlowType.TRANSFER; + this.updateTime = System.currentTimeMillis(); + this.snapshotId = snapshot.getId(); + this.bindClass = snapshot.getClazzName(); + this.opinion = opinion; + } + + + /** + * 转办产生的流程记录 + * + * @param title 标题 + * @param operator 操作者 + */ + public void transferToTodo(String title, IFlowOperator operator) { + this.id = 0; + this.flowType = FlowType.TODO; + this.flowStatus = FlowStatus.RUNNING; + this.postponedCount = 0; + this.updateTime = 0; + this.readTime = 0; + this.read = false; + this.title = title; + this.opinion = null; + this.flowSourceDirection = null; + this.interfere = false; + this.interferedOperator = null; + this.currentOperator = operator; + } + + /** + * 自动提交流程 (非会签时自通审批) + * + * @param flowOperator 操作者 + * @param snapshot 绑定数据 + */ + public void autoPass(IFlowOperator flowOperator, BindDataSnapshot snapshot) { + this.read(); + this.flowSourceDirection = FlowSourceDirection.PASS; + this.currentOperator = flowOperator; + this.flowType = FlowType.DONE; + this.updateTime = System.currentTimeMillis(); + this.snapshotId = snapshot.getId(); + this.bindClass = snapshot.getClazzName(); + this.opinion = Opinion.unSignAutoSuccess(); + } + + + /** + * 完成流程 + */ + public void finish() { + this.flowStatus = FlowStatus.FINISH; + this.finishTime = System.currentTimeMillis(); + } + + + /** + * 是否已审批 + */ + public boolean isDone() { + return this.flowType == FlowType.DONE; + } + + /** + * 是否完成 + */ + public boolean isFinish() { + return this.flowStatus == FlowStatus.FINISH; + } + + /** + * 是否是待办 + */ + public boolean isTodo() { + return this.flowType == FlowType.TODO && this.flowStatus == FlowStatus.RUNNING; + } + + /** + * 是否是转交 + * + * @return 是否是转交 + */ + public boolean isTransfer() { + return this.flowType == FlowType.TRANSFER; + } + + /** + * 审批通过 + */ + public boolean isPass() { + return this.opinion != null && this.opinion.isSuccess() && isDone(); + } + + /** + * 匹配操作者 + * + * @param currentOperator 当前操作者 + */ + public void matcherOperator(IFlowOperator currentOperator) { + if (!isOperator(currentOperator)) { + throw new IllegalArgumentException("current operator is not match"); + } + } + + /** + * 是否是当前操作者 + * + * @param operator 操作者 + * @return 是否是当前操作者 + */ + public boolean isOperator(IFlowOperator operator) { + return this.currentOperator.getUserId() == operator.getUserId(); + } + + + /** + * 撤回流程 + */ + public void recall() { + this.flowType = FlowType.TODO; + this.updateTime = System.currentTimeMillis(); + } + + + /** + * 复制流程记录 + */ + public FlowRecord copy() { + FlowRecord record = new FlowRecord(); + record.setId(this.id); + record.setPostponedCount(this.postponedCount); + record.setPreId(this.preId); + record.setWorkId(this.workId); + record.setProcessId(this.processId); + record.setNodeCode(this.nodeCode); + record.setTitle(this.title); + record.setCurrentOperator(this.currentOperator); + record.setFlowType(this.flowType); + record.setFlowSourceDirection(this.flowSourceDirection); + record.setCreateTime(this.createTime); + record.setUpdateTime(this.updateTime); + record.setFinishTime(this.finishTime); + record.setTimeoutTime(this.timeoutTime); + record.setCreateOperator(this.createOperator); + record.setOpinion(this.opinion); + record.setFlowStatus(this.flowStatus); + record.setErrMessage(this.errMessage); + record.setBindClass(this.bindClass); + record.setSnapshotId(this.snapshotId); + record.setRead(this.read); + record.setInterfere(this.interfere); + record.setInterferedOperator(this.interferedOperator); + record.setReadTime(this.readTime); + return record; + } + + + /** + * 是否超时 + */ + public boolean isTimeout() { + if (this.timeoutTime == 0) { + return false; + } + return System.currentTimeMillis() > this.timeoutTime; + } + + /** + * 是否延期 + */ + public boolean isPostponed() { + return this.postponedCount > 0; + } + + /** + * 是否是发起节点 + */ + public boolean isStartRecord() { + return this.preId == 0; + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowBackupRepository.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowBackupRepository.java new file mode 100644 index 00000000..5263f035 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowBackupRepository.java @@ -0,0 +1,33 @@ +package com.codingapi.springboot.flow.repository; + +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.record.FlowBackup; + +/** + * 流程备份仓库(流程快照仓库) + */ +public interface FlowBackupRepository { + + /** + * 备份流程 + * @param flowWork 流程 + * @return 备份对象 + */ + FlowBackup backup(FlowWork flowWork); + + /** + * 根据流程id和版本号获取备份 + * @param workId 流程id + * @param workVersion 版本号 + * @return 备份对象 + */ + FlowBackup getFlowBackupByWorkIdAndVersion(long workId,long workVersion); + + /** + * 根据备份id获取备份 + * @param backupId 备份id + * @return 备份对象 + */ + FlowBackup getFlowBackupById(long backupId); + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowBindDataRepository.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowBindDataRepository.java new file mode 100644 index 00000000..a56cb2c3 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowBindDataRepository.java @@ -0,0 +1,31 @@ +package com.codingapi.springboot.flow.repository; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; + +/** + * 流程绑定数据仓库 + * 流程绑定数据即,流程的表单数据 + */ +public interface FlowBindDataRepository { + + /** + * 保存数据 + * @param snapshot 数据 + */ + void save(BindDataSnapshot snapshot); + + /** + * 更新数据 + * @param snapshot 数据 + */ + void update(BindDataSnapshot snapshot); + + + /** + * 查询快照数据 + * @param id 快照id + * @return BindDataSnapshot + */ + BindDataSnapshot getBindDataSnapshotById(long id); + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowOperatorRepository.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowOperatorRepository.java new file mode 100644 index 00000000..4f73f7d9 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowOperatorRepository.java @@ -0,0 +1,29 @@ +package com.codingapi.springboot.flow.repository; + +import com.codingapi.springboot.flow.user.IFlowOperator; + +import java.util.List; + +/** + * 流程操作者 仓库 + */ +public interface FlowOperatorRepository { + + /** + * 根据ID查询流程用户 + * + * @param ids IDs + * @return List of IFlowOperator + */ + List findByIds(List ids); + + + /** + * 根据ID查询流程用户 + * + * @param id ID + * @return IFlowOperator + */ + IFlowOperator getFlowOperatorById(long id); + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowProcessRepository.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowProcessRepository.java new file mode 100644 index 00000000..568d35b5 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowProcessRepository.java @@ -0,0 +1,16 @@ +package com.codingapi.springboot.flow.repository; + +import com.codingapi.springboot.flow.record.FlowProcess; +import com.codingapi.springboot.flow.domain.FlowWork; + +/** + * 流程仓库,每一个流程都会在创建时被创建一条process数据,用于标识流程 + */ +public interface FlowProcessRepository { + + void save(FlowProcess flowProcess); + + + FlowWork getFlowWorkByProcessId(String processId); + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java new file mode 100644 index 00000000..623c420d --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java @@ -0,0 +1,70 @@ +package com.codingapi.springboot.flow.repository; + +import com.codingapi.springboot.flow.record.FlowRecord; + +import java.util.List; + + +/** + * 流转记录数据仓库 + */ +public interface FlowRecordRepository { + + /** + * 保存流程记录 + * + * @param records 流程记录 + */ + void save(List records); + + /** + * 更新流程记录 + * + * @param flowRecord 流程记录 + */ + void update(FlowRecord flowRecord); + + /** + * 根据ID查询流程记录 + * + * @param id 流程记录ID + * @return FlowRecord + */ + FlowRecord getFlowRecordById(long id); + + /** + * 根据前置ID查询流程记录 + * + * @param preId 前置ID + * @return List of FlowRecord + */ + List findFlowRecordByPreId(long preId); + + + /** + * 根据流程id查询流程记录 + * @param processId 流程id + */ + List findFlowRecordByProcessId(String processId); + + + /** + * 查询所有未完成的流程记录 + * @param processId 流程id + * @return List of FlowRecord + */ + List findTodoFlowRecordByProcessId(String processId); + + /** + * 根据流程id 修改所有的记录状态为已完成 + * + * @param processId 流程id + */ + void finishFlowRecordByProcessId(String processId); + + /** + * 删除流程记录 + * @param childrenRecords 流程记录 + */ + void delete(List childrenRecords); +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowWorkRepository.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowWorkRepository.java new file mode 100644 index 00000000..093e918e --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowWorkRepository.java @@ -0,0 +1,18 @@ +package com.codingapi.springboot.flow.repository; + +import com.codingapi.springboot.flow.domain.FlowWork; + +/** + * 流程设计器仓库 + */ +public interface FlowWorkRepository { + + FlowWork getFlowWorkById(long id); + + FlowWork getFlowWorkByCode(String code); + + void save(FlowWork flowWork); + + void delete(long id); + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/script/GroovyShellContext.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/script/GroovyShellContext.java new file mode 100644 index 00000000..356fbb6e --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/script/GroovyShellContext.java @@ -0,0 +1,87 @@ +package com.codingapi.springboot.flow.script; + +import com.codingapi.springboot.flow.utils.Sha256Utils; +import groovy.lang.GroovyShell; +import groovy.lang.Script; +import lombok.Getter; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class GroovyShellContext { + + @Getter + private final static GroovyShellContext instance = new GroovyShellContext(); + + private final static GroovyShell groovyShell = new GroovyShell(); + + private final Map cache = new HashMap<>(); + + private final static ExecutorService threadPool = Executors.newFixedThreadPool(10); + + // 缓存最大值 + private final static int MAX_CACHE_SIZE = 10000; + + + private GroovyShellContext() { + } + + public ShellScript parse(String script) { + String hash = Sha256Utils.generateSHA256(script); + if (cache.containsKey(hash)) { + return cache.get(hash); + } else { + if (cache.size() > MAX_CACHE_SIZE) { + cache.clear(); + } + ShellScript shellScript = new ShellScript(script); + threadPool.submit(shellScript); + cache.put(hash, shellScript); + return shellScript; + } + } + + + public int size() { + return cache.size(); + } + + public static class ShellScript implements Runnable { + + @Getter + private final String script; + + private Script runtime; + + public ShellScript(String script) { + this.script = script; + } + + public Object invokeMethod(String run, Object params) { + synchronized (ShellScript.this) { + if (runtime == null) { + try { + ShellScript.this.wait(1000); + } catch (InterruptedException ignored) { + } + if (runtime == null) { + runtime = groovyShell.parse(script); + } + } + return runtime.invokeMethod(run, params); + } + } + + @Override + public void run() { + runtime = groovyShell.parse(script); + synchronized (ShellScript.this) { + this.notifyAll(); + } + } + } + + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowNodeSerializable.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowNodeSerializable.java new file mode 100644 index 00000000..5e22c61e --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowNodeSerializable.java @@ -0,0 +1,94 @@ +package com.codingapi.springboot.flow.serializable; + +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.em.NodeType; +import com.codingapi.springboot.flow.error.ErrTrigger; +import com.codingapi.springboot.flow.generator.TitleGenerator; +import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; + +/** + * 流程节点序列化 + */ +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class FlowNodeSerializable implements Serializable { + + /** + * 节点id + */ + private String id; + + /** + * 节点编码 + */ + private String code; + + /** + * 节点名称 + */ + private String name; + + /** + * 节点标题创建规则 + */ + private String titleGenerator; + + /** + * 节点类型 | 分为发起、审批、结束 + */ + private NodeType type; + + /** + * 节点视图 + */ + private String view; + + /** + * 流程审批类型 | 分为会签、非会签 + */ + private ApprovalType approvalType; + + /** + * 操作者匹配器 + */ + private String operatorMatcher; + + /** + * 是否可编辑 + */ + private boolean editable; + + /** + * 创建时间 + */ + private long createTime; + /** + * 更新时间 + */ + private long updateTime; + + /** + * 超时时间(毫秒) + */ + private long timeout; + + /** + * 异常触发器,当流程发生异常时异常通常是指找不到审批人,将会触发异常触发器,异常触发器可以是一个节点 + */ + private String errTrigger; + + + public FlowNode toFlowNode() { + return new FlowNode(id, code, name, new TitleGenerator(titleGenerator), type, view, approvalType, + new OperatorMatcher(operatorMatcher), editable, createTime, updateTime, timeout, errTrigger == null ? null : new ErrTrigger(errTrigger)); + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowRelationSerializable.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowRelationSerializable.java new file mode 100644 index 00000000..d2e0e0aa --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowRelationSerializable.java @@ -0,0 +1,82 @@ +package com.codingapi.springboot.flow.serializable; + +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowRelation; +import com.codingapi.springboot.flow.trigger.OutTrigger; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; +import java.util.List; + +/** + * 流程关系序列化 + */ +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class FlowRelationSerializable implements Serializable { + + /** + * 关系id + */ + private String id; + + /** + * 名称 + */ + private String name; + + /** + * 源节点 + */ + private String sourceId; + + /** + * 目标节点 + */ + private String targetId; + + /** + * 排序 + */ + private int order; + + /** + * 是否退回 + */ + private boolean back; + + /** + * 出口触发器 + */ + private String outTrigger; + + + /** + * 创建时间 + */ + private long createTime; + + /** + * 修改时间 + */ + private long updateTime; + + public FlowRelation toFlowRelation(List nodes) { + FlowNode source = null; + FlowNode target = null; + for (FlowNode node : nodes) { + if (node.getId().equals(sourceId)) { + source = node; + } + if (node.getId().equals(targetId)) { + target = node; + } + } + return new FlowRelation(id, name, source, target, order, back, new OutTrigger(outTrigger), createTime, updateTime); + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowWorkSerializable.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowWorkSerializable.java new file mode 100644 index 00000000..050561a1 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowWorkSerializable.java @@ -0,0 +1,140 @@ +package com.codingapi.springboot.flow.serializable; + +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.em.NodeType; +import com.codingapi.springboot.flow.repository.FlowOperatorRepository; +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.ByteArrayOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 流程序列化 + * 流程序列化为了减少数据存储的压力,该数据会存储到FlowProcess的创建过程中,为了降低数据的存储容量,将会自动判断 + * 是否存在相同的版本,若存在时则不会存储新的数据。 + * + */ +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class FlowWorkSerializable implements Serializable { + + /** + * 流程的设计id + */ + private long id; + + /** + * 流程编号 + */ + private String code; + + /** + * 流程标题 + */ + private String title; + /** + * 流程描述 + */ + private String description; + /** + * 流程创建者 + */ + private long createUser; + /** + * 创建时间 + */ + private long createTime; + /** + * 更新时间 + */ + private long updateTime; + /** + * 是否启用 + */ + @Setter + private boolean enable; + + /** + * 最大延期次数 + */ + @Setter + private int postponedMax; + + /** + * 流程的节点(发起节点) + */ + private List nodes; + + /** + * 流程的关系 + */ + private List relations; + + + /** + * 序列化 + * + * @return 序列化对象 + */ + public byte[] toSerializable() { + Kryo kryo = new Kryo(); + kryo.register(ArrayList.class); + kryo.register(FlowNodeSerializable.class); + kryo.register(FlowRelationSerializable.class); + kryo.register(FlowWorkSerializable.class); + kryo.register(ApprovalType.class); + kryo.register(NodeType.class); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + Output output = new Output(outputStream); + kryo.writeObject(output, this); + output.close(); + return outputStream.toByteArray(); + } + + + public static FlowWorkSerializable fromSerializable(byte[] bytes) { + Kryo kryo = new Kryo(); + kryo.register(ArrayList.class); + kryo.register(FlowNodeSerializable.class); + kryo.register(FlowRelationSerializable.class); + kryo.register(FlowWorkSerializable.class); + kryo.register(ApprovalType.class); + kryo.register(NodeType.class); + return kryo.readObject(new Input(bytes), FlowWorkSerializable.class); + } + + + public FlowWork toFlowWork(FlowOperatorRepository flowOperatorRepository) { + List flowNodes = nodes.stream().map(FlowNodeSerializable::toFlowNode).collect(Collectors.toList()); + return new FlowWork( + id, + code, + title, + description, + flowOperatorRepository.getFlowOperatorById(createUser), + createTime, + updateTime, + enable, + postponedMax, + flowNodes, + relations.stream().map((item) -> item.toFlowRelation(flowNodes)).collect(Collectors.toList()), + null + ); + } + + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java new file mode 100644 index 00000000..2326a8fd --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java @@ -0,0 +1,126 @@ +package com.codingapi.springboot.flow.service; + +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.FlowSourceDirection; +import com.codingapi.springboot.flow.record.FlowRecord; +import lombok.Getter; + +import java.util.List; + +/** + * 流程方向服务 + */ +class FlowDirectionService { + + private final FlowWork flowWork; + private final FlowNode flowNode; + private final Opinion opinion; + + @Getter + private FlowSourceDirection flowSourceDirection; + + + private List historyRecords; + + public FlowDirectionService(FlowNode flowNode, FlowWork flowWork, Opinion opinion) { + this.flowNode = flowNode; + this.opinion = opinion; + this.flowWork = flowWork; + } + + + public void bindHistoryRecords(List historyRecords) { + this.historyRecords = historyRecords; + } + + + /** + * 解析当前的审批方向 + */ + public void loadFlowSourceDirection() { + if (opinion.isSuccess()) { + flowSourceDirection = FlowSourceDirection.PASS; + } + if (opinion.isReject()) { + flowSourceDirection = FlowSourceDirection.REJECT; + } + } + + + /** + * 重新加载审批方向 + * 根据会签结果判断是否需要重新设置审批方向 + */ + public FlowSourceDirection reloadFlowSourceDirection() { + if (flowNode.isSign()) { + boolean allPass = historyRecords.stream().filter(item -> !item.isTransfer()).allMatch(FlowRecord::isPass); + if (!allPass) { + flowSourceDirection = FlowSourceDirection.REJECT; + } + } + return flowSourceDirection; + } + + + /** + * 校验流程的审批方向 + */ + public void verifyFlowSourceDirection() { + if (flowSourceDirection == null) { + throw new IllegalArgumentException("flow source direction is null"); + } + if (flowNode.isStartNode() && flowSourceDirection == FlowSourceDirection.REJECT) { + throw new IllegalArgumentException("flow node is start node"); + } + } + + /** + * 判断当前流程节点是否已经完成,是否可以继续流转 + */ + public boolean hasCurrentFlowNodeIsDone() { + // 会签下所有人尚未提交时,不执行下一节点 + boolean allDone = historyRecords.stream().filter(item -> !item.isTransfer()).allMatch(FlowRecord::isDone); + if (!allDone) { + // 流程尚未审批结束直接退出 + return true; + } + return false; + } + + + /** + * 检测当前流程是否已经完成 + * 即流程已经进行到了最终节点且审批意见为同意 + */ + public boolean hasCurrentFlowIsFinish() { + if (flowSourceDirection == FlowSourceDirection.PASS && flowNode.isOverNode()) { + return true; + } + return false; + } + + + /** + * 判断当前流程是否为默认的驳回流程 + */ + public boolean isDefaultBackRecord() { + return flowSourceDirection == FlowSourceDirection.REJECT && !flowWork.hasBackRelation(); + } + + /** + * 判断当前流程是否为通过流程 + */ + public boolean isPassBackRecord() { + return flowSourceDirection == FlowSourceDirection.PASS; + } + + /** + * 判断当前流程是否为自定义的驳回流程 + */ + public boolean isCustomBackRecord() { + return flowSourceDirection == FlowSourceDirection.REJECT && flowWork.hasBackRelation(); + } + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordBuilderService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordBuilderService.java new file mode 100644 index 00000000..654ef87a --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordBuilderService.java @@ -0,0 +1,251 @@ +package com.codingapi.springboot.flow.service; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.content.FlowSession; +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowRelation; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.error.ErrorResult; +import com.codingapi.springboot.flow.error.NodeResult; +import com.codingapi.springboot.flow.error.OperatorResult; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.FlowOperatorRepository; +import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.user.IFlowOperator; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 流程记录构建服务 + */ +class FlowRecordBuilderService { + + + private final FlowOperatorRepository flowOperatorRepository; + private final FlowRecordRepository flowRecordRepository; + + private final String processId; + private final long preId; + + private final FlowWork flowWork; + private final Opinion opinion; + private final IFlowOperator currentOperator; + private final BindDataSnapshot snapshot; + private final List historyRecords; + private final IFlowOperator createOperator; + + + public FlowRecordBuilderService(FlowOperatorRepository flowOperatorRepository, + FlowRecordRepository flowRecordRepository, + BindDataSnapshot snapshot, + Opinion opinion, + IFlowOperator createOperator, + IFlowOperator currentOperator, + List historyRecords, + FlowWork flowWork, + String processId, + long preId) { + + if(createOperator==null){ + throw new IllegalArgumentException("createOperator is null"); + } + + this.createOperator = createOperator; + this.flowOperatorRepository = flowOperatorRepository; + + this.flowRecordRepository = flowRecordRepository; + this.snapshot = snapshot; + this.opinion = opinion; + this.currentOperator = currentOperator; + this.flowWork = flowWork; + + + this.processId = processId; + this.preId = preId; + this.historyRecords = historyRecords; + } + + + /** + * 获取下一个节点 + * + * @return 下一个节点 + */ + private FlowNode matcherNextNode(FlowNode flowNode, boolean back) { + List relations = flowWork.getRelations().stream() + .filter(relation -> relation.sourceMatcher(flowNode.getCode())) + .filter(relation -> relation.isBack() == back) + .sorted((o1, o2) -> (o2.getOrder() - o1.getOrder())) + .collect(Collectors.toList()); + if (relations.isEmpty()) { + throw new IllegalArgumentException("relation not found"); + } + FlowSession flowSession = new FlowSession(flowWork, flowNode, createOperator, currentOperator, snapshot.toBindData(), opinion, historyRecords); + List flowNodes = new ArrayList<>(); + for (FlowRelation flowRelation : relations) { + FlowNode node = flowRelation.trigger(flowSession); + if (node != null) { + flowNodes.add(node); + } + } + if (flowNodes.isEmpty()) { + throw new IllegalArgumentException("next node not found"); + } + return flowNodes.get(0); + } + + /** + * 异常匹配 + * + * @param currentNode 当前节点 + * @param currentOperator 当前操作者 + * @return 流程记录 + */ + private List errMatcher(FlowNode currentNode, IFlowOperator currentOperator) { + if (currentNode.hasErrTrigger()) { + FlowSession flowSession = new FlowSession(flowWork, currentNode, createOperator, currentOperator, snapshot.toBindData(), opinion, historyRecords); + ErrorResult errorResult = currentNode.errMatcher(flowSession); + if (errorResult == null) { + throw new IllegalArgumentException("errMatcher match error."); + } + + // 匹配操作者 + if (errorResult.isOperator()) { + List recordList = new ArrayList<>(); + List operatorIds = ((OperatorResult) errorResult).getOperatorIds(); + List operators = flowOperatorRepository.findByIds(operatorIds); + for (IFlowOperator operator : operators) { + FlowSession content = new FlowSession(flowWork, currentNode, createOperator, operator, snapshot.toBindData(), opinion, historyRecords); + String recordTitle = currentNode.generateTitle(content); + FlowRecord record = currentNode.createRecord(flowWork.getId(), processId, preId, recordTitle, createOperator, operator, snapshot); + recordList.add(record); + } + return recordList; + } + // 匹配节点 + if (errorResult.isNode()) { + String nodeCode = ((NodeResult) errorResult).getNode(); + FlowNode node = flowWork.getNodeByCode(nodeCode); + if (node == null) { + throw new IllegalArgumentException("node not found."); + } + List recordList = new ArrayList<>(); + FlowSession content = new FlowSession(flowWork, node, createOperator, currentOperator, snapshot.toBindData(), opinion, historyRecords); + List matcherOperators = node.loadFlowNodeOperator(content, flowOperatorRepository); + if (!matcherOperators.isEmpty()) { + for (IFlowOperator matcherOperator : matcherOperators) { + String recordTitle = node.generateTitle(content); + FlowRecord record = node.createRecord(flowWork.getId(), processId, preId, recordTitle, createOperator, matcherOperator, snapshot); + recordList.add(record); + } + } + return recordList; + } + throw new IllegalArgumentException("errMatcher not match."); + } + throw new IllegalArgumentException("operator not match."); + } + + + /** + * 创建流程记录 + * + * @param currentNode 当前节点 + * @return 流程记录 + */ + public List createRecord(FlowNode currentNode, IFlowOperator currentOperator) { + return this.createRecord(currentNode,currentOperator,null); + } + + /** + * 创建流程记录 + * + * @param currentNode 当前节点 + * @param currentOperator 当前审批人 + * @param opinion 审批意见 + * @return 流程记录 + */ + public List createRecord(FlowNode currentNode, IFlowOperator currentOperator,Opinion opinion) { + FlowSession flowSession = new FlowSession(flowWork, currentNode, createOperator, currentOperator, snapshot.toBindData(), opinion, historyRecords); + long workId = flowWork.getId(); + List operators = currentNode.loadFlowNodeOperator(flowSession, flowOperatorRepository); + List recordList; + if (operators.isEmpty()) { + recordList= this.errMatcher(currentNode, currentOperator); + if (recordList.isEmpty()) { + throw new IllegalArgumentException("operator not match."); + } + } else { + String recordTitle = currentNode.generateTitle(flowSession); + recordList = new ArrayList<>(); + for (IFlowOperator operator : operators) { + FlowRecord record = currentNode.createRecord(workId, processId, preId, recordTitle, createOperator, operator, snapshot); + recordList.add(record); + } + } + if(!recordList.isEmpty()){ + for (FlowRecord record:recordList){ + if(opinion!=null){ + record.updateOpinion(opinion); + } + } + } + + return recordList; + } + + + /** + * 创建下一个节点 + */ + public List createNextRecord(FlowNode flowNode) { + FlowNode nextNode = this.matcherNextNode(flowNode, false); + return this.createRecord(nextNode, currentOperator); + } + + /** + * 创建自定义的下级别节点 + */ + public List createCustomBackRecord(FlowNode flowNode, long parentRecordId) { + FlowNode nextNode = this.matcherNextNode(flowNode, true); + if (nextNode == null) { + throw new IllegalArgumentException("next node not found"); + } + IFlowOperator flowOperator = currentOperator; + if (nextNode.isAnyOperatorMatcher()) { + // 如果是任意人员操作时则需要指定为当时审批人员为当前审批人员 + FlowRecord preFlowRecord = flowRecordRepository.getFlowRecordById(parentRecordId); + while (preFlowRecord.isTransfer() || !preFlowRecord.getNodeCode().equals(nextNode.getCode())) { + preFlowRecord = flowRecordRepository.getFlowRecordById(preFlowRecord.getPreId()); + } + flowOperator = preFlowRecord.getCurrentOperator(); + } + return this.createRecord(nextNode, flowOperator); + } + + /** + * 创建默认拒绝时的流程记录 + */ + public List createDefaultBackRecord(long parentRecordId) { + IFlowOperator flowOperator; + // 拒绝时,默认返回上一个节点 + FlowRecord preRecord = flowRecordRepository.getFlowRecordById(parentRecordId); + // 去除所有的转办的记录 + while (preRecord.isTransfer()) { + // 继续寻找上一个节点 + preRecord = flowRecordRepository.getFlowRecordById(preRecord.getPreId()); + } + // 获取上一个节点的审批者,继续将审批者设置为当前审批者 + flowOperator = preRecord.getCurrentOperator(); + FlowNode nextNode = flowWork.getNodeByCode(preRecord.getNodeCode()); + if (nextNode == null) { + throw new IllegalArgumentException("next node not found"); + } + return this.createRecord(nextNode, flowOperator); + } + + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordService.java new file mode 100644 index 00000000..910529bb --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordService.java @@ -0,0 +1,192 @@ +package com.codingapi.springboot.flow.service; + +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.FlowProcessRepository; +import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.user.IFlowOperator; +import lombok.Getter; + +import java.util.List; + +/** + * 流程记录服务(流程内部服务) + */ +class FlowRecordService { + + // constructor params + private final long recordId; + @Getter + private final IFlowOperator currentOperator; + + // register repository + final FlowRecordRepository flowRecordRepository; + final FlowProcessRepository flowProcessRepository; + + // load Object + @Getter + private FlowWork flowWork; + @Getter + private FlowNode flowNode; + @Getter + private FlowRecord flowRecord; + + public FlowRecordService(FlowRecordRepository flowRecordRepository, + FlowProcessRepository flowProcessRepository, + long recordId, + IFlowOperator currentOperator) { + this.flowRecordRepository = flowRecordRepository; + this.flowProcessRepository = flowProcessRepository; + + this.currentOperator = currentOperator; + this.recordId = recordId; + } + + + + /** + * 校验流程记录是否已提交状态 + */ + public void verifyFlowRecordSubmitState() { + flowRecord.submitStateVerify(); + } + + /** + * 校验流程是否当前操作者可操作的 + */ + public void verifyFlowRecordCurrentOperator() { + if(!currentOperator.isFlowManager()) { + flowRecord.matcherOperator(currentOperator); + } + } + + /** + * 校验流程是否已审批 + */ + public void verifyFlowRecordNotDone() { + if (flowRecord.isDone()) { + throw new IllegalArgumentException("flow record is done"); + } + } + + + /** + * 校验流程是否已审批 + */ + public void verifyFlowRecordIsDone() { + if (!flowRecord.isDone()) { + throw new IllegalArgumentException("flow record is not done"); + } + } + + + + /** + * 校验流程是否未审批 + */ + public void verifyFlowRecordNotTodo() { + if (flowRecord.isTodo()) { + throw new IllegalArgumentException("flow record is todo"); + } + } + + /** + * 校验流程是未审批 + */ + public void verifyFlowRecordIsTodo() { + if (!flowRecord.isTodo()) { + throw new IllegalArgumentException("flow record is not todo"); + } + } + + /** + * 校验流程是否已完成 + */ + public void verifyFlowRecordNotFinish() { + if (flowRecord.isFinish()) { + throw new IllegalArgumentException("flow record is finish"); + } + } + + /** + * 校验流程节点是否可编辑 + */ + public void verifyFlowNodeEditableState(boolean editable) { + // 流程节点不可编辑时,不能保存 + if (flowNode.isEditable() == editable) { + throw new IllegalArgumentException("flow node is not editable"); + } + } + + + /** + * 校验转办人员不能是当前操作者 + */ + public void verifyTargetOperatorIsNotCurrentOperator(IFlowOperator targetOperator) { + if(currentOperator.getUserId() == targetOperator.getUserId()){ + throw new IllegalArgumentException("current operator is target operator"); + } + } + + + /** + * 获取流程记录对象 + */ + public void loadFlowRecord() { + FlowRecord flowRecord = flowRecordRepository.getFlowRecordById(recordId); + if (flowRecord == null) { + throw new IllegalArgumentException("flow record not found"); + } + this.flowRecord = flowRecord; + } + + /** + * 获取流程设计对象 + */ + public void loadFlowWork() { + FlowWork flowWork = flowProcessRepository.getFlowWorkByProcessId(flowRecord.getProcessId()); + if (flowWork == null) { + throw new IllegalArgumentException("flow work not found"); + } + flowWork.enableValidate(); + this.flowWork = flowWork; + } + + + /** + * 获取流程节点对象 + */ + public void loadFlowNode() { + FlowNode flowNode = flowWork.getNodeByCode(flowRecord.getNodeCode()); + if (flowNode == null) { + throw new IllegalArgumentException("flow node not found"); + } + this.flowNode = flowNode; + } + + /** + * 标记流程为已读状态 + */ + public void setFlowRecordRead() { + if (currentOperator != null) { + if(flowRecord.isOperator(currentOperator)) { + if (!flowRecord.isRead()) { + flowRecord.read(); + flowRecordRepository.update(flowRecord); + } + } + } + } + + /** + * 校验是否后续没有审批记录 + */ + public void verifyChildrenRecordsIsEmpty() { + List childrenRecords = flowRecordRepository.findFlowRecordByPreId(flowRecord.getId()); + if (!childrenRecords.isEmpty()) { + throw new IllegalArgumentException("flow node is done"); + } + } + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java new file mode 100644 index 00000000..25309652 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java @@ -0,0 +1,522 @@ +package com.codingapi.springboot.flow.service; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.bind.IBindData; +import com.codingapi.springboot.flow.content.FlowSession; +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.FlowSourceDirection; +import com.codingapi.springboot.flow.event.FlowApprovalEvent; +import com.codingapi.springboot.flow.pojo.FlowDetail; +import com.codingapi.springboot.flow.pojo.FlowResult; +import com.codingapi.springboot.flow.record.FlowBackup; +import com.codingapi.springboot.flow.record.FlowProcess; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.*; +import com.codingapi.springboot.flow.user.IFlowOperator; +import com.codingapi.springboot.framework.event.EventPusher; +import lombok.AllArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + + +/** + * 流程服务 + */ +@Transactional +@AllArgsConstructor +public class FlowService { + + + private final FlowWorkRepository flowWorkRepository; + private final FlowRecordRepository flowRecordRepository; + private final FlowBindDataRepository flowBindDataRepository; + private final FlowOperatorRepository flowOperatorRepository; + private final FlowProcessRepository flowProcessRepository; + private final FlowBackupRepository flowBackupRepository; + + + /** + * 流程详情 + * + * @param recordId 流程记录id + * @return 流程详情 + */ + public FlowDetail detail(long recordId) { + return detail(recordId, null); + } + + /** + * 延期待办 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + * @param time 延期时间 + */ + public void postponed(long recordId, IFlowOperator currentOperator, long time) { + FlowRecordService flowRecordService = new FlowRecordService(flowRecordRepository, + flowProcessRepository, + recordId, currentOperator); + + flowRecordService.loadFlowRecord(); + flowRecordService.verifyFlowRecordSubmitState(); + flowRecordService.verifyFlowRecordCurrentOperator(); + flowRecordService.loadFlowWork(); + flowRecordService.verifyFlowRecordNotFinish(); + flowRecordService.verifyFlowRecordNotDone(); + + FlowRecord flowRecord = flowRecordService.getFlowRecord(); + FlowWork flowWork = flowRecordService.getFlowWork(); + + flowRecord.postponedTime(flowWork.getPostponedMax(), time); + flowRecordRepository.update(flowRecord); + } + + /** + * 催办流程 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + */ + public void urge(long recordId, IFlowOperator currentOperator) { + FlowRecordService flowRecordService = new FlowRecordService(flowRecordRepository, + flowProcessRepository, + recordId, currentOperator); + flowRecordService.loadFlowRecord(); + flowRecordService.loadFlowWork(); + flowRecordService.verifyFlowRecordIsDone(); + + FlowRecord flowRecord = flowRecordService.getFlowRecord(); + FlowWork flowWork = flowRecordService.getFlowWork(); + + List todoRecords = flowRecordRepository.findTodoFlowRecordByProcessId(flowRecord.getProcessId()); + + // 推送催办消息 + for (FlowRecord record : todoRecords) { + IFlowOperator pushOperator = record.getCurrentOperator(); + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_URGE, record, pushOperator, flowWork, null)); + } + + } + + /** + * 流程详情 + * 如果传递了currentOperator为流程的审批者时,在查看详情的时候可以将流程记录标记为已读 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + */ + public FlowDetail detail(long recordId, IFlowOperator currentOperator) { + + FlowRecordService flowRecordService = new FlowRecordService(flowRecordRepository, + flowProcessRepository, + recordId, currentOperator); + + flowRecordService.loadFlowRecord(); + flowRecordService.setFlowRecordRead(); + flowRecordService.loadFlowWork(); + + FlowRecord flowRecord = flowRecordService.getFlowRecord(); + FlowWork flowWork = flowRecordService.getFlowWork(); + + + BindDataSnapshot snapshot = flowBindDataRepository.getBindDataSnapshotById(flowRecord.getSnapshotId()); + List flowRecords = + flowRecordRepository.findFlowRecordByProcessId(flowRecord.getProcessId()). + stream(). + sorted((o1, o2) -> (int) (o2.getId() - o1.getId())) + .collect(Collectors.toList()); + + List operators = new ArrayList<>(); + // 获取所有的操作者 + for (FlowRecord record : flowRecords) { + operators.add(record.getCreateOperator()); + operators.add(record.getCurrentOperator()); + operators.add(record.getInterferedOperator()); + } + + return new FlowDetail(flowRecord, snapshot, flowWork, flowRecords, operators); + } + + + /** + * 干预流程 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + * @param bindData 绑定数据 + * @param opinion 审批意见 + */ + public FlowResult interfere(long recordId, IFlowOperator currentOperator, IBindData bindData, Opinion opinion) { + if (!currentOperator.isFlowManager()) { + throw new IllegalArgumentException("current operator is not flow manager"); + } + return this.submitFlow(recordId, currentOperator, bindData, opinion); + } + + + /** + * 转办流程 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + * @param targetOperator 转办操作者 + * @param bindData 绑定数据 + * @param advice 转办意见 + */ + public void transfer(long recordId, IFlowOperator currentOperator, IFlowOperator targetOperator, IBindData bindData, String advice) { + + FlowRecordService flowRecordService = new FlowRecordService(flowRecordRepository, + flowProcessRepository, + recordId, currentOperator); + + flowRecordService.loadFlowRecord(); + flowRecordService.verifyFlowRecordSubmitState(); + flowRecordService.verifyFlowRecordCurrentOperator(); + flowRecordService.verifyTargetOperatorIsNotCurrentOperator(targetOperator); + + flowRecordService.loadFlowWork(); + flowRecordService.loadFlowNode(); + + flowRecordService.verifyFlowRecordIsTodo(); + + FlowRecord flowRecord = flowRecordService.getFlowRecord(); + FlowWork flowWork = flowRecordService.getFlowWork(); + FlowNode flowNode = flowRecordService.getFlowNode(); + + + // 保存绑定数据 + BindDataSnapshot snapshot = new BindDataSnapshot(bindData); + flowBindDataRepository.save(snapshot); + + // 构建审批意见 + Opinion opinion = Opinion.transfer(advice); + + // 设置自己的流程状态为转办已完成 + flowRecord.transfer(currentOperator, snapshot, opinion); + flowRecordRepository.update(flowRecord); + + // 获取创建者 + IFlowOperator createOperator = flowRecord.getCreateOperator(); + + // 与当前流程同级的流程记录 + List historyRecords; + if (flowRecord.isStartRecord()) { + historyRecords = new ArrayList<>(); + } else { + historyRecords = flowRecordRepository.findFlowRecordByPreId(flowRecord.getPreId()); + } + + // 创建新的待办标题 + FlowSession content = new FlowSession(flowWork, flowNode, createOperator, targetOperator, snapshot.toBindData(), opinion, historyRecords); + String generateTitle = flowNode.generateTitle(content); + + // 创建转办记录 + FlowRecord transferRecord = flowRecord.copy(); + transferRecord.transferToTodo(generateTitle, targetOperator); + flowRecordRepository.save(Collections.singletonList(transferRecord)); + + // 推送转办消息 + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TRANSFER, flowRecord, currentOperator, flowWork, snapshot.toBindData())); + + // 推送待办消息 + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, transferRecord, targetOperator, flowWork, snapshot.toBindData())); + } + + + /** + * 保存流程 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + * @param bindData 绑定数据 + * @param advice 审批意见 + */ + public void save(long recordId, IFlowOperator currentOperator, IBindData bindData, String advice) { + FlowRecordService flowRecordService = new FlowRecordService(flowRecordRepository, + flowProcessRepository, + recordId, currentOperator); + flowRecordService.loadFlowRecord(); + flowRecordService.verifyFlowRecordSubmitState(); + flowRecordService.verifyFlowRecordCurrentOperator(); + flowRecordService.loadFlowWork(); + flowRecordService.loadFlowNode(); + flowRecordService.verifyFlowNodeEditableState(false); + + Opinion opinion = Opinion.save(advice); + FlowRecord flowRecord = flowRecordService.getFlowRecord(); + BindDataSnapshot snapshot = new BindDataSnapshot(flowRecord.getSnapshotId(), bindData); + flowBindDataRepository.update(snapshot); + + flowRecord.setOpinion(opinion); + flowRecordService.flowRecordRepository.update(flowRecord); + + } + + + /** + * 发起流程 (不自动提交到下一节点) + * + * @param workCode 流程编码 + * @param operator 操作者 + * @param bindData 绑定数据 + * @param advice 审批意见 + */ + public FlowResult startFlow(String workCode, IFlowOperator operator, IBindData bindData, String advice) { + // 检测流程是否存在 + FlowWork flowWork = flowWorkRepository.getFlowWorkByCode(workCode); + if (flowWork == null) { + throw new IllegalArgumentException("flow work not found"); + } + flowWork.verify(); + flowWork.enableValidate(); + + // 流程数据备份 + FlowBackup flowBackup = flowBackupRepository.getFlowBackupByWorkIdAndVersion(flowWork.getId(), flowWork.getUpdateTime()); + if (flowBackup == null) { + flowBackup = flowBackupRepository.backup(flowWork); + } + + // 保存流程 + FlowProcess flowProcess = new FlowProcess(flowBackup.getId(), operator); + flowProcessRepository.save(flowProcess); + + // 保存绑定数据 + BindDataSnapshot snapshot = new BindDataSnapshot(bindData); + flowBindDataRepository.save(snapshot); + + // 创建流程id + String processId = flowProcess.getProcessId(); + + // 构建审批意见 + Opinion opinion = Opinion.pass(advice); + + // 获取开始节点 + FlowNode start = flowWork.getStartNode(); + if (start == null) { + throw new IllegalArgumentException("start node not found"); + } + // 设置开始流程的上一个流程id + long preId = 0; + + List historyRecords = new ArrayList<>(); + + FlowRecordBuilderService flowRecordBuilderService = new FlowRecordBuilderService( + flowOperatorRepository, + flowRecordRepository, + snapshot, + opinion, + operator, + operator, + historyRecords, + flowWork, + processId, + preId + ); + + // 创建待办记录 + List records = flowRecordBuilderService.createRecord(start, operator, opinion); + if (records.isEmpty()) { + throw new IllegalArgumentException("flow record not found"); + } + // 保存流程记录 + flowRecordRepository.save(records); + + // 推送事件消息 + for (FlowRecord record : records) { + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_CREATE, record, operator, flowWork, snapshot.toBindData())); + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, record, operator, flowWork, snapshot.toBindData())); + } + // 当前的审批记录 + return new FlowResult(flowWork, records); + } + + /** + * 提交流程 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + * @param bindData 绑定数据 + * @param opinion 审批意见 + */ + public FlowResult submitFlow(long recordId, IFlowOperator currentOperator, IBindData bindData, Opinion opinion) { + + FlowRecordService flowRecordService = new FlowRecordService(flowRecordRepository, flowProcessRepository, recordId, currentOperator); + + // 加载流程 + flowRecordService.loadFlowRecord(); + // 验证流程的提交状态 + flowRecordService.verifyFlowRecordSubmitState(); + // 验证当前操作者 + flowRecordService.verifyFlowRecordCurrentOperator(); + // 加载流程设计 + flowRecordService.loadFlowWork(); + // 加载流程节点 + flowRecordService.loadFlowNode(); + // 验证没有子流程 + flowRecordService.verifyChildrenRecordsIsEmpty(); + + // 获取流程记录对象 + FlowRecord flowRecord = flowRecordService.getFlowRecord(); + FlowNode flowNode = flowRecordService.getFlowNode(); + FlowWork flowWork = flowRecordService.getFlowWork(); + + + // 保存流程表单快照数据 + BindDataSnapshot snapshot = null; + if (flowNode.isEditable()) { + snapshot = new BindDataSnapshot(bindData); + flowBindDataRepository.save(snapshot); + } else { + snapshot = flowBindDataRepository.getBindDataSnapshotById(flowRecord.getSnapshotId()); + } + + // 审批方向判断服务 + FlowDirectionService flowDirectionService = new FlowDirectionService(flowRecordService.getFlowNode(), flowRecordService.getFlowWork(), opinion); + + // 加载流程审批方向 + flowDirectionService.loadFlowSourceDirection(); + // 验证审批方向 + flowDirectionService.verifyFlowSourceDirection(); + + // 根据当前方向提交流程 + FlowSourceDirection flowSourceDirection = flowDirectionService.getFlowSourceDirection(); + flowRecord.submitRecord(currentOperator, snapshot, opinion, flowSourceDirection); + flowRecordRepository.update(flowRecord); + + // 与当前流程同级的流程记录 + List historyRecords; + if (flowRecord.isStartRecord()) { + historyRecords = new ArrayList<>(); + } else { + historyRecords = flowRecordRepository.findFlowRecordByPreId(flowRecord.getPreId()); + } + flowDirectionService.bindHistoryRecords(historyRecords); + + // 判断流程是否结束(会签时需要所有人都通过) + if (flowNode.isSign()) { + boolean next = flowDirectionService.hasCurrentFlowNodeIsDone(); + if (next) { + List todoRecords = historyRecords.stream().filter(FlowRecord::isTodo).collect(Collectors.toList()); + return new FlowResult(flowWork, todoRecords); + } + } + + // 非会签下,当有人提交以后,将所有未提交的流程都自动提交,然后再执行下一节点 + if (flowNode.isUnSign()) { + for (FlowRecord record : historyRecords) { + if (record.isTodo() && record.getId() != flowRecord.getId()) { + record.autoPass(currentOperator, snapshot); + flowRecordRepository.update(flowRecord); + } + } + } + + // 根据所有提交意见,重新加载审批方向 + flowSourceDirection = flowDirectionService.reloadFlowSourceDirection(); + + + // 判断流程是否完成 + if (flowDirectionService.hasCurrentFlowIsFinish()) { + flowRecord.finish(); + flowRecord.submitRecord(currentOperator, snapshot, opinion, flowSourceDirection); + flowRecordRepository.update(flowRecord); + flowRecordRepository.finishFlowRecordByProcessId(flowRecord.getProcessId()); + + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_FINISH, flowRecord, currentOperator, flowWork, snapshot.toBindData())); + return new FlowResult(flowWork, flowRecord); + } + + // 获取流程的发起者 + IFlowOperator createOperator = flowRecord.getCreateOperator(); + + // 构建流程创建器 + FlowRecordBuilderService flowRecordBuilderService = new FlowRecordBuilderService( + flowOperatorRepository, + flowRecordRepository, + snapshot, + opinion, + createOperator, + currentOperator, + historyRecords, + flowWork, + flowRecord.getProcessId(), + flowRecord.getId() + ); + + // 创建下一节点的流程记录 + List records; + // 审批通过并进入下一节点 + if (flowDirectionService.isPassBackRecord()) { + records = flowRecordBuilderService.createNextRecord(flowNode); + // 审批拒绝返回上一节点 + } else if (flowDirectionService.isDefaultBackRecord()) { + records = flowRecordBuilderService.createDefaultBackRecord(flowRecord.getPreId()); + } else { + // 审批拒绝,并且自定了返回节点 + records = flowRecordBuilderService.createCustomBackRecord(flowNode, flowRecord.getPreId()); + } + + // 保存流程记录 + flowRecordRepository.save(records); + + // 推送审批事件消息 + int eventState = flowSourceDirection == FlowSourceDirection.PASS ? FlowApprovalEvent.STATE_PASS : FlowApprovalEvent.STATE_REJECT; + EventPusher.push(new FlowApprovalEvent(eventState, flowRecord, currentOperator, flowWork, snapshot.toBindData())); + + // 推送待办事件消息 + for (FlowRecord record : records) { + IFlowOperator pushOperator = record.getCurrentOperator(); + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, record, pushOperator, flowWork, snapshot.toBindData())); + } + + return new FlowResult(flowWork, records); + } + + + /** + * 撤回流程 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + */ + public void recall(long recordId, IFlowOperator currentOperator) { + FlowRecordService flowRecordService = new FlowRecordService(flowRecordRepository, + flowProcessRepository, + recordId, currentOperator); + + flowRecordService.loadFlowRecord(); + flowRecordService.verifyFlowRecordCurrentOperator(); + flowRecordService.loadFlowWork(); + flowRecordService.loadFlowNode(); + flowRecordService.verifyFlowRecordNotFinish(); + flowRecordService.verifyFlowRecordNotTodo(); + + FlowRecord flowRecord = flowRecordService.getFlowRecord(); + FlowWork flowWork = flowRecordService.getFlowWork(); + + // 下一流程的流程记录 + List childrenRecords = flowRecordRepository.findFlowRecordByPreId(recordId); + // 下一流程均为办理且未读 + + if (childrenRecords.isEmpty()) { + throw new IllegalArgumentException("flow record not submit"); + } + + boolean allUnDone = childrenRecords.stream().allMatch(item -> item.isUnRead() && item.isTodo()); + if (!allUnDone) { + throw new IllegalArgumentException("flow record not recall"); + } + flowRecord.recall(); + flowRecordRepository.update(flowRecord); + + flowRecordRepository.delete(childrenRecords); + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_RECALL, flowRecord, currentOperator, flowWork, null)); + } + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/trigger/OutTrigger.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/trigger/OutTrigger.java new file mode 100644 index 00000000..b7281495 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/trigger/OutTrigger.java @@ -0,0 +1,44 @@ +package com.codingapi.springboot.flow.trigger; + +import com.codingapi.springboot.flow.content.FlowSession; +import com.codingapi.springboot.flow.script.GroovyShellContext; +import lombok.Getter; +import org.springframework.util.StringUtils; + +/** + * 出口触发器 + */ +public class OutTrigger { + + @Getter + private final String script; + + private final GroovyShellContext.ShellScript runtime; + + public OutTrigger(String script) { + if (!StringUtils.hasLength(script)) { + throw new IllegalArgumentException("script is empty"); + } + this.script = script; + this.runtime = GroovyShellContext.getInstance().parse(script); + } + + /** + * 默认出口触发器 + */ + public static OutTrigger defaultOutTrigger() { + return new OutTrigger("def run(content) {return true;}"); + } + + + /** + * 触发 + * + * @param flowSession 流程内容 + * @return true 进入下一节点,false 则返回上一节点 + */ + public boolean trigger(FlowSession flowSession) { + return (Boolean) runtime.invokeMethod("run", flowSession); + } + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/user/IFlowOperator.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/user/IFlowOperator.java new file mode 100644 index 00000000..69dfa44d --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/user/IFlowOperator.java @@ -0,0 +1,36 @@ +package com.codingapi.springboot.flow.user; + +/** + * 流程参与用户 + */ +public interface IFlowOperator { + + /** + * 获取用户ID + * + * @return ID + */ + long getUserId(); + + + /** + * 获取用户名称 + * @return 名称 + */ + String getName(); + + + /** + * 是否流程管理员 + * 流程管理员可以强制干预流程 + */ + boolean isFlowManager(); + + + /** + * 委托操作者 + * 当委托操作者不为空时,当前操作者将由委托操作者执行 + */ + IFlowOperator entrustOperator(); + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/utils/Sha256Utils.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/utils/Sha256Utils.java new file mode 100644 index 00000000..c7a37b0b --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/utils/Sha256Utils.java @@ -0,0 +1,25 @@ +package com.codingapi.springboot.flow.utils; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class Sha256Utils { + + + public static String generateSHA256(String input) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(input.getBytes()); + StringBuilder hexString = new StringBuilder(); + for (byte b : hash) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) hexString.append('0'); + hexString.append(hex); + } + return hexString.toString(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/springboot-starter-flow/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/springboot-starter-flow/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..3a985208 --- /dev/null +++ b/springboot-starter-flow/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.codingapi.springboot.flow.FlowConfiguration diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/FlowTestApplication.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/FlowTestApplication.java new file mode 100644 index 00000000..8a5c85d0 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/FlowTestApplication.java @@ -0,0 +1,12 @@ +package com.codingapi.springboot.flow; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class FlowTestApplication { + + public static void main(String[] args) { + SpringApplication.run(FlowTestApplication.class, args); + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave.java new file mode 100644 index 00000000..2d02fed0 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave.java @@ -0,0 +1,24 @@ +package com.codingapi.springboot.flow.flow; + +import com.codingapi.springboot.flow.bind.IBindData; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class Leave implements IBindData { + + private long id; + private String title; + private int days; + + public Leave(String title) { + this(title,0); + } + + public Leave(String title, int days) { + this.title = title; + this.days = days; + } + +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowBackupRepositoryImpl.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowBackupRepositoryImpl.java new file mode 100644 index 00000000..0efbb8cd --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowBackupRepositoryImpl.java @@ -0,0 +1,29 @@ +package com.codingapi.springboot.flow.repository; + +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.record.FlowBackup; + +import java.util.ArrayList; +import java.util.List; + +public class FlowBackupRepositoryImpl implements FlowBackupRepository{ + + private final List cache = new ArrayList<>(); + + @Override + public FlowBackup backup(FlowWork flowWork) { + FlowBackup flowBackup = new FlowBackup(flowWork); + cache.add(flowBackup); + return flowBackup; + } + + @Override + public FlowBackup getFlowBackupByWorkIdAndVersion(long workId, long workVersion) { + return cache.stream().filter(flowBackup -> flowBackup.getWorkId() == workId && flowBackup.getWorkVersion() == workVersion).findFirst().orElse(null); + } + + @Override + public FlowBackup getFlowBackupById(long backupId) { + return cache.stream().filter(flowBackup -> flowBackup.getId() == backupId).findFirst().orElse(null); + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowBindDataRepositoryImpl.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowBindDataRepositoryImpl.java new file mode 100644 index 00000000..2f3b8228 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowBindDataRepositoryImpl.java @@ -0,0 +1,38 @@ +package com.codingapi.springboot.flow.repository; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import lombok.AllArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +@AllArgsConstructor +public class FlowBindDataRepositoryImpl implements FlowBindDataRepository { + + private final List cache = new ArrayList<>(); + + @Override + public void save(BindDataSnapshot snapshot) { + if (snapshot.getId() == 0) { + cache.add(snapshot); + snapshot.setId(cache.size()); + } + } + + @Override + public void update(BindDataSnapshot snapshot) { + BindDataSnapshot old = getBindDataSnapshotById(snapshot.getId()); + if (old != null) { + old.setSnapshot(snapshot.getSnapshot()); + } + } + + @Override + public BindDataSnapshot getBindDataSnapshotById(long id) { + return cache.stream().filter(snapshot -> snapshot.getId() == id).findFirst().orElse(null); + } + + public List findAll(){ + return cache; + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowProcessRepositoryImpl.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowProcessRepositoryImpl.java new file mode 100644 index 00000000..782f0bae --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowProcessRepositoryImpl.java @@ -0,0 +1,39 @@ +package com.codingapi.springboot.flow.repository; + +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.record.FlowBackup; +import com.codingapi.springboot.flow.record.FlowProcess; +import lombok.AllArgsConstructor; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@AllArgsConstructor +public class FlowProcessRepositoryImpl implements FlowProcessRepository { + + private final List cache = new ArrayList<>(); + private final FlowBackupRepository flowBackupRepository; + private final UserRepository userRepository; + + + @Override + public void save(FlowProcess flowProcess) { + List ids = cache.stream().map(FlowProcess::getProcessId).collect(Collectors.toList()); + if (!ids.contains(flowProcess.getProcessId())) { + cache.add(flowProcess); + } + } + + @Override + public FlowWork getFlowWorkByProcessId(String processId) { + FlowProcess process = cache.stream().filter(flowProcess -> flowProcess.getProcessId().equals(processId)).findFirst().orElse(null); + if (process == null) { + return null; + } + FlowBackup flowBackup = flowBackupRepository.getFlowBackupById(process.getBackupId()); + return flowBackup.resume(userRepository); + } + + +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java new file mode 100644 index 00000000..5875e22d --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java @@ -0,0 +1,104 @@ +package com.codingapi.springboot.flow.repository; + +import com.codingapi.springboot.flow.query.FlowRecordQuery; +import com.codingapi.springboot.flow.record.FlowRecord; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class FlowRecordRepositoryImpl implements FlowRecordRepository, FlowRecordQuery { + + private final List cache = new ArrayList<>(); + + @Override + public void save(List records) { + for (FlowRecord record : records) { + if (record.getId() == 0) { + cache.add(record); + record.setId(cache.size()); + } + } + } + + @Override + public FlowRecord getFlowRecordById(long id) { + return cache.stream().filter(record -> record.getId() == id).findFirst().orElse(null); + } + + + @Override + public void update(FlowRecord flowRecord) { + if (flowRecord.getId() == 0) { + cache.add(flowRecord); + flowRecord.setId(cache.size()); + } + } + + @Override + public List findFlowRecordByPreId(long preId) { + return cache.stream().filter(record -> record.getPreId() == preId).collect(Collectors.toList()); + } + + @Override + public List findFlowRecordByProcessId(String processId) { + return cache.stream().filter(record -> record.getProcessId().equals(processId)) + .sorted((o1, o2) -> (int) (o1.getCreateTime() - o2.getCreateTime())) + .collect(Collectors.toList()); + } + + @Override + public List findTodoFlowRecordByProcessId(String processId) { + return cache.stream().filter(record -> record.isTodo() && record.getProcessId().equals(processId)).collect(Collectors.toList()); + } + + public Page findAll(PageRequest pageRequest) { + return new PageImpl<>(cache); + } + + @Override + public Page findDoneByOperatorId(long operatorId,PageRequest pageRequest) { + List flowRecords = cache.stream().filter(record -> record.isDone() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList()); + return new PageImpl<>(flowRecords); + } + + @Override + public Page findInitiatedByOperatorId(long operatorId,PageRequest pageRequest) { + List flowRecords = cache.stream().filter(record -> record.isInitiated() && record.getCreateOperator().getUserId() == operatorId).collect(Collectors.toList()); + return new PageImpl<>(flowRecords); + } + + @Override + public Page findTodoByOperatorId(long operatorId,PageRequest pageRequest) { + List flowRecords = cache.stream().filter(record -> record.isTodo() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList()); + return new PageImpl<>(flowRecords); + } + + @Override + public Page findTimeoutTodoByOperatorId(long operatorId,PageRequest pageRequest) { + List flowRecords = cache.stream().filter(record -> record.isTimeout() && record.isTodo() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList()); + return new PageImpl<>(flowRecords); + } + + + @Override + public Page findPostponedTodoByOperatorId(long operatorId,PageRequest pageRequest) { + List flowRecords = cache.stream().filter(record -> record.isPostponed() && record.isTodo() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList()); + return new PageImpl<>(flowRecords); + } + + @Override + public void finishFlowRecordByProcessId(String processId) { + cache.stream() + .filter(record -> record.getProcessId().equals(processId)) + .forEach(FlowRecord::finish); + } + + @Override + public void delete(List childrenRecords) { + cache.removeAll(childrenRecords); + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowWorkRepositoryImpl.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowWorkRepositoryImpl.java new file mode 100644 index 00000000..6e93dbea --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowWorkRepositoryImpl.java @@ -0,0 +1,34 @@ +package com.codingapi.springboot.flow.repository; + +import com.codingapi.springboot.flow.domain.FlowWork; + +import java.util.ArrayList; +import java.util.List; + +public class FlowWorkRepositoryImpl implements FlowWorkRepository{ + + private final List cache = new ArrayList<>(); + + @Override + public FlowWork getFlowWorkById(long id) { + return cache.stream().filter(flowWork -> flowWork.getId() == id).findFirst().orElse(null); + } + + @Override + public FlowWork getFlowWorkByCode(String code) { + return cache.stream().filter(flowWork -> flowWork.getCode().equals(code)).findFirst().orElse(null); + } + + @Override + public void save(FlowWork flowWork) { + if(flowWork.getId()==0){ + cache.add(flowWork); + flowWork.setId(cache.size()); + } + } + + @Override + public void delete(long id) { + cache.removeIf(flowWork -> flowWork.getId() == id); + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/LeaveRepository.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/LeaveRepository.java new file mode 100644 index 00000000..dd4f50f1 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/LeaveRepository.java @@ -0,0 +1,18 @@ +package com.codingapi.springboot.flow.repository; + +import com.codingapi.springboot.flow.flow.Leave; + +import java.util.ArrayList; +import java.util.List; + +public class LeaveRepository { + + private final List cache = new ArrayList<>(); + + public void save(Leave leave) { + if (leave.getId() == 0) { + cache.add(leave); + leave.setId(cache.size()); + } + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/UserRepository.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/UserRepository.java new file mode 100644 index 00000000..315bc040 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/UserRepository.java @@ -0,0 +1,39 @@ +package com.codingapi.springboot.flow.repository; + +import com.codingapi.springboot.flow.user.IFlowOperator; +import com.codingapi.springboot.flow.user.User; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class UserRepository implements FlowOperatorRepository { + + private final List cache = new ArrayList<>(); + + public void save(User user) { + if (user.getId() == 0) { + cache.add(user); + user.setId(cache.size()); + } + } + + public User getById(long id) { + for (User user : cache) { + if (user.getId() == id) { + return user; + } + } + return null; + } + + @Override + public IFlowOperator getFlowOperatorById(long createOperatorId) { + return getById(createOperatorId); + } + + @Override + public List findByIds(List ids) { + return cache.stream().filter(user -> ids.contains(user.getId())).collect(Collectors.toList()); + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/script/GroovyShellContextTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/script/GroovyShellContextTest.java new file mode 100644 index 00000000..80f655fd --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/script/GroovyShellContextTest.java @@ -0,0 +1,30 @@ +package com.codingapi.springboot.flow.script; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class GroovyShellContextTest { + + @Test + void getInstance() { + long t1 = System.currentTimeMillis(); + int count = 12000; + GroovyShellContext.ShellScript[] scripts = new GroovyShellContext.ShellScript[count]; + for (int i = 0; i < count; i++) { + scripts[i] = GroovyShellContext.getInstance().parse("def run(content){ return '" + i + "';}"); + } + + long t2 = System.currentTimeMillis(); + System.out.println("t2 time :" + (t2 - t1)); + System.out.println("size:" + GroovyShellContext.getInstance().size()); + + for (int i = 0; i < count; i++) { + assertEquals(scripts[i].invokeMethod("run", i), String.valueOf(i)); + } + + long t3 = System.currentTimeMillis(); + System.out.println("t3 time :" + (t3 - t2)); + + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/BuildTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/BuildTest.java new file mode 100644 index 00000000..47c426a6 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/BuildTest.java @@ -0,0 +1,49 @@ +package com.codingapi.springboot.flow.test; + +import com.codingapi.springboot.flow.build.FlowWorkBuilder; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.repository.UserRepository; +import com.codingapi.springboot.flow.serializable.FlowWorkSerializable; +import com.codingapi.springboot.flow.user.User; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class BuildTest { + + private final UserRepository userRepository = new UserRepository(); + + + @Test + void build() { + User user = new User("张三"); + userRepository.save(user); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + assertEquals("请假流程", flowWork.getTitle()); + assertEquals(4, flowWork.getNodes().size()); + assertEquals(3, flowWork.getRelations().size()); + + + byte[] bytes = flowWork.toSerializable().toSerializable(); + FlowWorkSerializable flowWorkSerializable = FlowWorkSerializable.fromSerializable(bytes); + assertEquals("请假流程", flowWorkSerializable.getTitle()); + + FlowWork serializableWork = flowWorkSerializable.toFlowWork(userRepository); + assertEquals("请假流程", serializableWork.getTitle()); + + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ErrorTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ErrorTest.java new file mode 100644 index 00000000..219b8472 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ErrorTest.java @@ -0,0 +1,218 @@ +package com.codingapi.springboot.flow.test; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.build.FlowWorkBuilder; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.error.ErrTrigger; +import com.codingapi.springboot.flow.flow.Leave; +import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.pojo.FlowDetail; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.*; +import com.codingapi.springboot.flow.service.FlowService; +import com.codingapi.springboot.flow.user.User; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ErrorTest { + + private final UserRepository userRepository = new UserRepository(); + private final FlowWorkRepository flowWorkRepository = new FlowWorkRepositoryImpl(); + private final FlowRecordRepositoryImpl flowRecordRepository = new FlowRecordRepositoryImpl(); + private final FlowBindDataRepositoryImpl flowBindDataRepository = new FlowBindDataRepositoryImpl(); + private final LeaveRepository leaveRepository = new LeaveRepository(); + private final FlowBackupRepository flowBackupRepository = new FlowBackupRepositoryImpl(); + private final FlowProcessRepository flowProcessRepository = new FlowProcessRepositoryImpl(flowBackupRepository,userRepository); + private final FlowService flowService = new FlowService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, userRepository,flowProcessRepository,flowBackupRepository); + + + /** + * 异常节点触发器 + * 异常时配置其他人来审批 + */ + @Test + void errorMatcherOperatorTest(){ + PageRequest pageRequest = PageRequest.of(0, 1000); + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, new OperatorMatcher("def run(content){return []}"), new ErrTrigger("def run(content){return content.createOperatorErrTrigger("+dept.getId()+")}"), true) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + // 保存流程 + leave.setTitle("我要出去看看~~"); + flowService.save(userTodo.getId(), user, leave,"暂存"); + + // 查看流程详情 + FlowDetail flowDetail = flowService.detail(userTodo.getId(), user); + assertEquals("我要出去看看~~", ((Leave)flowDetail.getBindData()).getTitle()); + assertTrue(flowDetail.getFlowRecord().isRead()); + + + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(5, snapshots.size()); + } + + + + /** + * 异常节点触发器 + * 异常时配置其节点来审批 + */ + @Test + void errorMatcherNodeTest(){ + + PageRequest pageRequest = PageRequest.of(0,1000); + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, new OperatorMatcher("def run(content){return []}"), new ErrTrigger("def run(content){return content.createNodeErrTrigger('manager')}"), true) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + // 保存流程 + leave.setTitle("我要出去看看~~"); + flowService.save(userTodo.getId(), user, leave,"暂存"); + + // 查看流程详情 + FlowDetail flowDetail = flowService.detail(userTodo.getId(), user); + assertEquals("我要出去看看~~", ((Leave)flowDetail.getBindData()).getTitle()); + assertTrue(flowDetail.getFlowRecord().isRead()); + + + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(0, deptTodos.size()); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(3, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(3, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(4, snapshots.size()); + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java new file mode 100644 index 00000000..40bcb8b4 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java @@ -0,0 +1,821 @@ +package com.codingapi.springboot.flow.test; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.build.FlowWorkBuilder; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.flow.Leave; +import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.pojo.FlowDetail; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.*; +import com.codingapi.springboot.flow.service.FlowService; +import com.codingapi.springboot.flow.user.User; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class FlowTest { + + private final UserRepository userRepository = new UserRepository(); + private final FlowWorkRepository flowWorkRepository = new FlowWorkRepositoryImpl(); + private final FlowRecordRepositoryImpl flowRecordRepository = new FlowRecordRepositoryImpl(); + private final FlowBindDataRepositoryImpl flowBindDataRepository = new FlowBindDataRepositoryImpl(); + private final LeaveRepository leaveRepository = new LeaveRepository(); + private final FlowBackupRepository flowBackupRepository = new FlowBackupRepositoryImpl(); + private final FlowProcessRepository flowProcessRepository = new FlowProcessRepositoryImpl(flowBackupRepository,userRepository); + private final FlowService flowService = new FlowService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, userRepository,flowProcessRepository,flowBackupRepository); + + /** + * 委托测试测试 + */ + @Test + void entrustTest() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User lorne = new User("lorne"); + userRepository.save(lorne); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备", lorne); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + assertEquals(0, userTodo.getTimeoutTime()); + // 保存流程 + leave.setTitle("我要出去看看~~"); + flowService.save(userTodo.getId(), user, leave,"暂存"); + + // 查看流程详情 + FlowDetail flowDetail = flowService.detail(userTodo.getId(), user); + assertEquals("我要出去看看~~", ((Leave) flowDetail.getBindData()).getTitle()); + assertTrue(flowDetail.getFlowRecord().isRead()); + + + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看刘备经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(0, deptTodos.size()); + + List lorneTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent(); + assertEquals(1, lorneTodos.size()); + + // 提交委托lorne部门经理的审批 + FlowRecord lorneTodo = lorneTodos.get(0); + flowService.submitFlow(lorneTodo.getId(), lorne, leave, Opinion.pass("同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(5, snapshots.size()); + + } + + + + /** + * 同意再拒绝 + */ + @Test + void passAndRejectTest() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("总经理审批", "manager", "default", ApprovalType.SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("开始节点", "start", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(1, records.size()); + + + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(3, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(4, snapshots.size()); + + } + + /** + * 全部通过测试 + */ + @Test + void passTest() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + // 保存流程 + leave.setTitle("我要出去看看~~"); + flowService.save(userTodo.getId(), user, leave,"暂存"); + + // 查看流程详情 + FlowDetail flowDetail = flowService.detail(userTodo.getId(), user); + assertEquals("我要出去看看~~", ((Leave) flowDetail.getBindData()).getTitle()); + assertTrue(flowDetail.getFlowRecord().isRead()); + + + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(5, snapshots.size()); + + } + + + /** + * 节点禁止保存通过测试 + */ + @Test + void saveDisableTest() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId()), false) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + leave.setTitle("我要出去看看~~"); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + assertEquals("我要出去看看", ((Leave) flowService.detail(deptTodo.getId()).getBindData()).getTitle()); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(4, snapshots.size()); + + } + + /** + * 干预流程 + */ + @Test + void interfereTest() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User admin = new User("lorne", true); + userRepository.save(admin); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + flowService.interfere(userTodo.getId(), admin, leave, Opinion.pass("同意")); + assertTrue(userTodo.isInterfere()); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.interfere(deptTodo.getId(), admin, leave, Opinion.pass("同意")); + assertTrue(deptTodo.isInterfere()); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.interfere(bossTodo.getId(), admin, leave, Opinion.pass("同意")); + assertTrue(bossTodo.isInterfere()); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(5, snapshots.size()); + } + + + /** + * 转办流程 + */ + @Test + void transferTest() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User lorne = new User("lorne"); + userRepository.save(lorne); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + + // 转交给lorne处理 + FlowRecord deptTodo = deptTodos.get(0); + flowService.transfer(deptTodo.getId(), dept, lorne, leave, "转办给lorne"); + + assertTrue(deptTodo.isTransfer()); + + deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(0, deptTodos.size()); + + List lorneTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent(); + assertEquals(1, lorneTodos.size()); + + FlowRecord lorneTodo = lorneTodos.get(0); + + flowService.submitFlow(lorneTodo.getId(), lorne, leave, Opinion.pass("同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(5, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(5, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(6, snapshots.size()); + } + + + /** + * 催办与延期测试 + */ + @Test + void postponedAndUrgeTest() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + + // 查看流程详情 + FlowDetail flowDetail = flowService.detail(userTodo.getId()); + assertEquals("我要出去看看", ((Leave) flowDetail.getBindData()).getTitle()); + assertTrue(flowDetail.getFlowRecord().isUnRead()); + + + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + + FlowRecord deptTodo = deptTodos.get(0); + + + // 延期10000毫米 + flowService.postponed(deptTodo.getId(), dept, 10000); + + long latestTimeOutTime = deptTodo.getTimeoutTime(); + long currentTime = System.currentTimeMillis(); + + assertTrue(latestTimeOutTime - currentTime >= 10000); + + // 再延期将会出现异常 + assertThrows(Exception.class, () -> flowService.postponed(deptTodo.getId(), dept, 10000)); + + // 催办 + flowService.urge(userTodo.getId(), user); + + // 待办下催办出现异常 + assertThrows(Exception.class, () -> flowService.postponed(deptTodo.getId(), dept, 10000)); + + } + + /** + * 部门拒绝再提交测试 + */ + @Test + void rejectTest() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + + // 查看流程详情 + FlowDetail flowDetail = flowService.detail(userTodo.getId()); + assertEquals("我要出去看看", ((Leave) flowDetail.getBindData()).getTitle()); + assertTrue(flowDetail.getFlowRecord().isUnRead()); + + + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.reject("不同意")); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(6, records.size()); + + + } + + + /** + * 撤销流程测试 + */ + @Test + void recallTest() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 撤销流程 + flowService.recall(userTodo.getId(), user); + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.reject("不同意")); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(6, records.size()); + + + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java new file mode 100644 index 00000000..ecc58e5c --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java @@ -0,0 +1,113 @@ +package com.codingapi.springboot.flow.test; + +import com.codingapi.springboot.flow.build.FlowWorkBuilder; +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.flow.Leave; +import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.*; +import com.codingapi.springboot.flow.service.FlowService; +import com.codingapi.springboot.flow.user.User; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class FlowTest2 { + + private final UserRepository userRepository = new UserRepository(); + private final FlowWorkRepository flowWorkRepository = new FlowWorkRepositoryImpl(); + private final FlowRecordRepositoryImpl flowRecordRepository = new FlowRecordRepositoryImpl(); + private final FlowBindDataRepositoryImpl flowBindDataRepository = new FlowBindDataRepositoryImpl(); + private final LeaveRepository leaveRepository = new LeaveRepository(); + private final FlowBackupRepository flowBackupRepository = new FlowBackupRepositoryImpl(); + private final FlowProcessRepository flowProcessRepository = new FlowProcessRepositoryImpl(flowBackupRepository,userRepository); + private final FlowService flowService = new FlowService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, userRepository,flowProcessRepository,flowBackupRepository); + + /** + * flow test + */ + @Test + void flowTest() { + PageRequest pageRequest = PageRequest.of(0, 1000); + User lorne = new User("lorne"); + userRepository.save(lorne); + + User boss = new User("boss"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(lorne) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("老板审批", "boss", "default", ApprovalType.SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .relations() + .relation("老板审批", "start", "boss") + .relation("结束节点", "boss", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我想要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, lorne, leave, "发起流程"); + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), lorne, leave, Opinion.pass("自己提交")); + + // 部门领导审批 + List deptTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + FlowRecord deptTodo = deptTodos.get(0); + assertNull(deptTodo.getOpinion()); + flowService.transfer(deptTodo.getId(), lorne,boss, leave, "转交给领导审批通过"); + + // 查看boss的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.reject("领导审批不通过")); + + userTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + assertEquals(FlowNode.CODE_START, userTodo.getNodeCode()); + + flowService.submitFlow(userTodo.getId(), lorne, leave, Opinion.pass("自己再次提交")); + + deptTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), lorne, leave, Opinion.pass("转交给领导审批通过")); + + bossTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), lorne, leave, Opinion.pass("领导审批通过")); + + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(6, records.size()); + + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/MultiRelationFlowTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/MultiRelationFlowTest.java new file mode 100644 index 00000000..f9d09469 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/MultiRelationFlowTest.java @@ -0,0 +1,314 @@ +package com.codingapi.springboot.flow.test; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.build.FlowWorkBuilder; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.flow.Leave; +import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.*; +import com.codingapi.springboot.flow.service.FlowService; +import com.codingapi.springboot.flow.trigger.OutTrigger; +import com.codingapi.springboot.flow.user.User; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MultiRelationFlowTest { + + private final UserRepository userRepository = new UserRepository(); + private final FlowWorkRepository flowWorkRepository = new FlowWorkRepositoryImpl(); + private final FlowRecordRepositoryImpl flowRecordRepository = new FlowRecordRepositoryImpl(); + private final FlowBindDataRepositoryImpl flowBindDataRepository = new FlowBindDataRepositoryImpl(); + private final LeaveRepository leaveRepository = new LeaveRepository(); + private final FlowBackupRepository flowBackupRepository = new FlowBackupRepositoryImpl(); + private final FlowProcessRepository flowProcessRepository = new FlowProcessRepositoryImpl(flowBackupRepository,userRepository); + private final FlowService flowService = new FlowService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, userRepository,flowProcessRepository,flowBackupRepository); + + /** + * 多条件流程测试 + * (直接走结束,没有流转老板测试) + */ + @Test + void relationTest1(){ + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "over",new OutTrigger("def run(content){content.getBindData().getDays()<=5}"),1,false) + .relation("总经理审批", "dept", "manager",new OutTrigger("def run(content){content.getBindData().getDays()>5}"),2,false) + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看",5); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(3, records.size()); + + // 最终用户确认 + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(3, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(4, snapshots.size()); + } + + + + + /** + * 多条件流程测试 + * (流转老板测试) + */ + @Test + void relationTest2(){ + + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "over",new OutTrigger("def run(content){content.getBindData().getDays()<=5}"),1,false) + .relation("总经理审批", "dept", "manager",new OutTrigger("def run(content){content.getBindData().getDays()>5}"),2,false) + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看",6); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + + // 查看老板的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交老板的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + + // 最终用户确认 + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(5, snapshots.size()); + } + + + + /** + * 多条件流程测试撤回 + */ + @Test + void relationTest3(){ + + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "over",new OutTrigger("def run(content){content.getBindData().getDays()<=5}"),1,false) + .relation("总经理审批", "dept", "manager",new OutTrigger("def run(content){content.getBindData().getDays()>5}"),2,false) + .relation("结束节点", "manager", "start",new OutTrigger("def run(content){return true}"),1,true) + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看",6); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + + // 查看老板的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交老板的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.reject("不同意,最多让你请假3天")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + + // 用户修改确认 + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 用户调整为3天 + leave.setDays(3); + // 提交流程 + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(5, records.size()); + + deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + // 用户修改确认 + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(7, snapshots.size()); + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/QueryTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/QueryTest.java new file mode 100644 index 00000000..d975ec01 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/QueryTest.java @@ -0,0 +1,540 @@ +package com.codingapi.springboot.flow.test; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.build.FlowWorkBuilder; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.flow.Leave; +import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.pojo.FlowDetail; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.*; +import com.codingapi.springboot.flow.service.FlowService; +import com.codingapi.springboot.flow.user.User; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class QueryTest { + + private final UserRepository userRepository = new UserRepository(); + private final FlowWorkRepository flowWorkRepository = new FlowWorkRepositoryImpl(); + private final FlowRecordRepositoryImpl flowRecordRepository = new FlowRecordRepositoryImpl(); + private final FlowBindDataRepositoryImpl flowBindDataRepository = new FlowBindDataRepositoryImpl(); + private final LeaveRepository leaveRepository = new LeaveRepository(); + private final FlowBackupRepository flowBackupRepository = new FlowBackupRepositoryImpl(); + private final FlowProcessRepository flowProcessRepository = new FlowProcessRepositoryImpl(flowBackupRepository,userRepository); + private final FlowService flowService = new FlowService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, userRepository,flowProcessRepository,flowBackupRepository); + + /** + * 查询用户的待办 + */ + @Test + void queryUserToDo(){ + + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(5, snapshots.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(0, userTodos.size()); + + deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(0, deptTodos.size()); + + bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(0, bossTodos.size()); + + } + + + /** + * 查询用户的超时待办 + */ + @Test + void queryUserTimeoutTodo(){ + + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher(),100,true) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 查看我的超时待办 + List userTimeOutTodos = flowRecordRepository.findTimeoutTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(0, userTimeOutTodos.size()); + + try { + Thread.sleep(200); + } catch (InterruptedException ignore) {} + + userTimeOutTodos = flowRecordRepository.findTimeoutTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTimeOutTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent();; + assertEquals(4, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(5, snapshots.size()); + + userTodos = flowRecordRepository.findTimeoutTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(0, userTodos.size()); + + deptTodos = flowRecordRepository.findTimeoutTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(0, deptTodos.size()); + + bossTodos = flowRecordRepository.findTimeoutTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(0, bossTodos.size()); + + } + + + + /** + * 查询用户的延期待办 + */ + @Test + void queryUserPostponedTodo(){ + + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher(),100,true) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 延期待办 + FlowRecord userTodo = userTodos.get(0); + flowService.postponed(userTodo.getId(), user,100); + + // 查看我的延期待办 + List userPostponedTodos = flowRecordRepository.findPostponedTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userPostponedTodos.size()); + + // 查看我的超时待办 + List userTimeOutTodos = flowRecordRepository.findTimeoutTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(0, userTimeOutTodos.size()); + + try { + Thread.sleep(200); + } catch (InterruptedException ignore) {} + + userTimeOutTodos = flowRecordRepository.findTimeoutTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTimeOutTodos.size()); + + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(5, snapshots.size()); + + userTodos = flowRecordRepository.findPostponedTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(0, userTodos.size()); + + deptTodos = flowRecordRepository.findPostponedTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(0, deptTodos.size()); + + bossTodos = flowRecordRepository.findPostponedTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(0, bossTodos.size()); + + } + + + /** + * 查询用户的已办 + */ + @Test + void queryUserDone(){ + + PageRequest pageRequest = PageRequest.of(0, 1000); + + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + FlowDetail flowDetail = flowService.detail(records.get(0).getId(), user); + assertEquals(4, flowDetail.getHistoryRecords().size()); + assertEquals(4, flowDetail.getOpinions().size()); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(5, snapshots.size()); + + + List userDones = flowRecordRepository.findDoneByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(2, userDones.size()); + + List deptDones = flowRecordRepository.findDoneByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptDones.size()); + + List bossDones = flowRecordRepository.findDoneByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossDones.size()); + + } + + + /** + * 查询用户发起的流程 + */ + @Test + void queryUserInitiated(){ + + PageRequest pageRequest = PageRequest.of(0, 1000); + + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(4, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(5, snapshots.size()); + + + + List userInitiates = flowRecordRepository.findInitiatedByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userInitiates.size()); + + List deptInitiates = flowRecordRepository.findInitiatedByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(0, deptInitiates.size()); + + List bossInitiates = flowRecordRepository.findInitiatedByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(0, bossInitiates.size()); + + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptBuildTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptBuildTest.java new file mode 100644 index 00000000..ce99c991 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptBuildTest.java @@ -0,0 +1,43 @@ +package com.codingapi.springboot.flow.test; + +import com.codingapi.springboot.flow.build.FlowWorkBuilder; +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.user.User; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class ScriptBuildTest { + + + @Test + void copy() { + User user = new User("张三"); + String script = "{\"nodes\":[{\"id\":\"e7699cab-e20b-4606-af66-fb8d782fd0f8\",\"type\":\"start-node\",\"x\":1005,\"y\":153,\"properties\":{\"name\":\"开始节点\",\"code\":\"start\",\"type\":\"START\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCreateOperator().getUserId()];}\",\"editable\":true,\"titleGenerator\":\"def run(content){ return content.getCreateOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"UN_SIGN\",\"timeout\":0,\"id\":\"e7699cab-e20b-4606-af66-fb8d782fd0f8\",\"width\":200,\"height\":45,\"operatorMatcherType\":\"creator\",\"titleGeneratorType\":\"default\",\"errTriggerType\":\"custom\"}},{\"id\":\"0d6638e9-0f98-45de-a5ad-8d55702158f7\",\"type\":\"node-node\",\"x\":722,\"y\":392,\"properties\":{\"name\":\"部门审批\",\"code\":\"dept\",\"type\":\"APPROVAL\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCurrentOperator().getUserId()];}\",\"editable\":true,\"titleGenerator\":\"def run(content){ return content.getCreateOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"SIGN\",\"timeout\":0,\"id\":\"0d6638e9-0f98-45de-a5ad-8d55702158f7\",\"width\":200,\"height\":45}},{\"id\":\"c4f8dd1b-53f2-4a87-8f57-9828b79fc4d0\",\"type\":\"over-node\",\"x\":918,\"y\":773,\"properties\":{\"name\":\"结束节点\",\"code\":\"over\",\"type\":\"OVER\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCurrentOperator().getUserId()];}\",\"editable\":true,\"titleGenerator\":\"def run(content){ return content.getCreateOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"UN_SIGN\",\"timeout\":0,\"id\":\"c4f8dd1b-53f2-4a87-8f57-9828b79fc4d0\",\"width\":200,\"height\":45}},{\"id\":\"ed654f48-c94c-4fdd-9b14-91f6295ae17a\",\"type\":\"node-node\",\"x\":1227,\"y\":530,\"properties\":{\"name\":\"老板审批\",\"code\":\"boss\",\"type\":\"APPROVAL\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCurrentOperator().getUserId()];}\",\"editable\":true,\"titleGenerator\":\"def run(content){ return content.getCreateOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"SIGN\",\"timeout\":0,\"id\":\"ed654f48-c94c-4fdd-9b14-91f6295ae17a\",\"width\":200,\"height\":45}}],\"edges\":[{\"id\":\"6854a4ef-89cf-48d3-9aa7-af1e5b87e93c\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":2,\"back\":false},\"sourceNodeId\":\"e7699cab-e20b-4606-af66-fb8d782fd0f8\",\"targetNodeId\":\"0d6638e9-0f98-45de-a5ad-8d55702158f7\",\"startPoint\":{\"x\":1005,\"y\":175.5},\"endPoint\":{\"x\":722,\"y\":369.5},\"pointsList\":[{\"x\":1005,\"y\":175.5},{\"x\":1005,\"y\":275.5},{\"x\":722,\"y\":269.5},{\"x\":722,\"y\":369.5}]},{\"id\":\"7c2c01fc-ded1-46e7-baf7-9fa664898d74\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":1,\"back\":false},\"sourceNodeId\":\"0d6638e9-0f98-45de-a5ad-8d55702158f7\",\"targetNodeId\":\"c4f8dd1b-53f2-4a87-8f57-9828b79fc4d0\",\"startPoint\":{\"x\":722,\"y\":414.5},\"endPoint\":{\"x\":918,\"y\":750.5},\"pointsList\":[{\"x\":722,\"y\":414.5},{\"x\":722,\"y\":514.5},{\"x\":918,\"y\":650.5},{\"x\":918,\"y\":750.5}]},{\"id\":\"67ee0fe7-b88c-4fde-be82-e23276f567e6\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":1,\"back\":false},\"sourceNodeId\":\"e7699cab-e20b-4606-af66-fb8d782fd0f8\",\"targetNodeId\":\"ed654f48-c94c-4fdd-9b14-91f6295ae17a\",\"startPoint\":{\"x\":1005,\"y\":175.5},\"endPoint\":{\"x\":1227,\"y\":507.5},\"pointsList\":[{\"x\":1005,\"y\":175.5},{\"x\":1005,\"y\":275.5},{\"x\":1227,\"y\":407.5},{\"x\":1227,\"y\":507.5}]},{\"id\":\"9c6f3b0c-03f5-46eb-be39-c79528a824dc\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":1,\"back\":false},\"sourceNodeId\":\"ed654f48-c94c-4fdd-9b14-91f6295ae17a\",\"targetNodeId\":\"c4f8dd1b-53f2-4a87-8f57-9828b79fc4d0\",\"startPoint\":{\"x\":1227,\"y\":552.5},\"endPoint\":{\"x\":918,\"y\":750.5},\"pointsList\":[{\"x\":1227,\"y\":552.5},{\"x\":1227,\"y\":652.5},{\"x\":918,\"y\":650.5},{\"x\":918,\"y\":750.5}]}]}"; + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .schema(script) + .build(); + assertEquals("请假流程", flowWork.getTitle()); + assertEquals(4, flowWork.getNodes().size()); + assertEquals(4, flowWork.getRelations().size()); + + + FlowNode startNode = flowWork.getStartNode(); + + FlowWork copyWork = flowWork.copy(); + assertNotEquals(copyWork.getCode(), flowWork.getCode()); + + assertEquals("请假流程", copyWork.getTitle()); + assertEquals(4, copyWork.getNodes().size()); + assertEquals(4, copyWork.getRelations().size()); + + copyWork.verify(); + + FlowNode startNode2 = copyWork.getNodeByCode("start"); + assertNotEquals(startNode.getId(), startNode2.getId()); + + + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptTest.java new file mode 100644 index 00000000..d2f9d9e7 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptTest.java @@ -0,0 +1,72 @@ +package com.codingapi.springboot.flow.test; + +import com.codingapi.springboot.flow.build.FlowWorkBuilder; +import com.codingapi.springboot.flow.content.FlowSession; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.flow.Leave; +import com.codingapi.springboot.flow.generator.TitleGenerator; +import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.repository.UserRepository; +import com.codingapi.springboot.flow.trigger.OutTrigger; +import com.codingapi.springboot.flow.user.User; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ScriptTest { + + private final UserRepository userRepository = new UserRepository(); + + @Test + void test() { + User user = new User("张三"); + userRepository.save(user); + + OperatorMatcher matcher = OperatorMatcher.anyOperatorMatcher(); + + OperatorMatcher operatorMatcher = OperatorMatcher.anyOperatorMatcher(); + + TitleGenerator titleGenerator = TitleGenerator.defaultTitleGenerator(); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, operatorMatcher) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, operatorMatcher) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, operatorMatcher) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, operatorMatcher) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + OutTrigger outTrigger =OutTrigger.defaultOutTrigger(); + OperatorMatcher specifyOperatorMatcher = OperatorMatcher.specifyOperatorMatcher(1); + + long now = System.currentTimeMillis(); + Leave leave = new Leave("我要请假"); + + FlowSession flowSession = new FlowSession(flowWork, flowWork.getNodeByCode("start"), user, user, leave, Opinion.pass("同意"),new ArrayList<>()); + + List ids = matcher.matcher(flowSession); + assertTrue(ids.contains(user.getUserId())); + + String title = titleGenerator.generate(flowSession); + assertEquals("张三-请假流程-开始节点", title); + + boolean next = outTrigger.trigger(flowSession); + assertTrue(next); + + List userIds = specifyOperatorMatcher.matcher(flowSession); + assertTrue(userIds.contains(1L)); + + long time = System.currentTimeMillis() - now; + System.out.println("time:" + time); + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/SignTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/SignTest.java new file mode 100644 index 00000000..d5128b83 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/SignTest.java @@ -0,0 +1,415 @@ +package com.codingapi.springboot.flow.test; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.build.FlowWorkBuilder; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.flow.Leave; +import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.*; +import com.codingapi.springboot.flow.service.FlowService; +import com.codingapi.springboot.flow.user.User; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class SignTest { + + + private final UserRepository userRepository = new UserRepository(); + private final FlowWorkRepository flowWorkRepository = new FlowWorkRepositoryImpl(); + private final FlowRecordRepositoryImpl flowRecordRepository = new FlowRecordRepositoryImpl(); + private final FlowBindDataRepositoryImpl flowBindDataRepository = new FlowBindDataRepositoryImpl(); + private final LeaveRepository leaveRepository = new LeaveRepository(); + private final FlowBackupRepository flowBackupRepository = new FlowBackupRepositoryImpl(); + private final FlowProcessRepository flowProcessRepository = new FlowProcessRepositoryImpl(flowBackupRepository,userRepository); + private final FlowService flowService = new FlowService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, userRepository,flowProcessRepository,flowBackupRepository); + + /** + * 多人非会签测试 + */ + @Test + void unSignTest(){ + PageRequest pageRequest = PageRequest.of(0, 1000); + + User caocao = new User("曹操"); + userRepository.save(caocao); + User lvBu = new User("吕布"); + userRepository.save(lvBu); + User zhaoYun = new User("赵云"); + userRepository.save(zhaoYun); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, + OperatorMatcher.specifyOperatorMatcher(dept.getUserId(),caocao.getUserId(),lvBu.getUserId(),zhaoYun.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(zhaoYun.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), zhaoYun, leave, Opinion.pass("同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent();; + assertEquals(7, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(7, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(5, snapshots.size()); + + } + + + + /** + * 多人会签测试 + */ + @Test + void signTest(){ + PageRequest pageRequest = PageRequest.of(0, 1000); + + User caocao = new User("曹操"); + userRepository.save(caocao); + User lvBu = new User("吕布"); + userRepository.save(lvBu); + User zhaoYun = new User("赵云"); + userRepository.save(zhaoYun); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.SIGN, + OperatorMatcher.specifyOperatorMatcher(dept.getUserId(),caocao.getUserId(),lvBu.getUserId(),zhaoYun.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("用户同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("刘备同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(0, bossTodos.size()); + + // 查看部门经理 吕布 的待办 + List lvbuTodos = flowRecordRepository.findTodoByOperatorId(lvBu.getUserId(), pageRequest).getContent(); + assertEquals(1, lvbuTodos.size()); + + // 提交部门经理 吕布 的审批 + FlowRecord lvbuTodo = lvbuTodos.get(0); + flowService.submitFlow(lvbuTodo.getId(), lvBu, leave, Opinion.pass("吕布同意")); + + + // 查看部门经理 赵云 的待办 + List zhaoYunTodos = flowRecordRepository.findTodoByOperatorId(zhaoYun.getUserId(), pageRequest).getContent(); + assertEquals(1, zhaoYunTodos.size()); + + // 提交部门经理 赵云 的审批 + FlowRecord zhaoYunTodo = zhaoYunTodos.get(0); + flowService.submitFlow(zhaoYunTodo.getId(), zhaoYun, leave, Opinion.pass("赵云同意")); + + + // 查看部门经理 曹操 的待办 + List caocaoTodos = flowRecordRepository.findTodoByOperatorId(caocao.getUserId(), pageRequest).getContent(); + assertEquals(1, caocaoTodos.size()); + + // 提交部门经理 曹操 的审批 + FlowRecord caocaoTodo = caocaoTodos.get(0); + flowService.submitFlow(caocaoTodo.getId(), caocao, leave, Opinion.pass("曹操同意")); + + bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(7, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(7, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(8, snapshots.size()); + + } + + + + + /** + * 多人会签 有人拒绝测试 + */ + @Test + void signRejectTest(){ + + PageRequest pageRequest = PageRequest.of(0, 1000); + + User caocao = new User("曹操"); + userRepository.save(caocao); + User lvBu = new User("吕布"); + userRepository.save(lvBu); + User zhaoYun = new User("赵云"); + userRepository.save(zhaoYun); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.SIGN, + OperatorMatcher.specifyOperatorMatcher( + dept.getUserId(), + caocao.getUserId(), + lvBu.getUserId(), + zhaoYun.getUserId() + )) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("用户同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.reject("刘备不同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(0, bossTodos.size()); + + // 查看部门经理 吕布 的待办 + List lvbuTodos = flowRecordRepository.findTodoByOperatorId(lvBu.getUserId(), pageRequest).getContent(); + assertEquals(1, lvbuTodos.size()); + + // 提交部门经理 吕布 的审批 + FlowRecord lvbuTodo = lvbuTodos.get(0); + flowService.submitFlow(lvbuTodo.getId(), lvBu, leave, Opinion.pass("吕布同意")); + + + // 查看部门经理 赵云 的待办 + List zhaoYunTodos = flowRecordRepository.findTodoByOperatorId(zhaoYun.getUserId(), pageRequest).getContent(); + assertEquals(1, zhaoYunTodos.size()); + + // 提交部门经理 赵云 的审批 + FlowRecord zhaoYunTodo = zhaoYunTodos.get(0); + flowService.submitFlow(zhaoYunTodo.getId(), zhaoYun, leave, Opinion.pass("赵云同意")); + + + // 查看部门经理 曹操 的待办 + List caocaoTodos = flowRecordRepository.findTodoByOperatorId(caocao.getUserId(), pageRequest).getContent(); + assertEquals(1, caocaoTodos.size()); + + // 提交部门经理 曹操 的审批 + FlowRecord caocaoTodo = caocaoTodos.get(0); + flowService.submitFlow(caocaoTodo.getId(), caocao, leave, Opinion.pass("曹操同意")); + + bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(0, bossTodos.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("用户同意")); + + + deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("刘备同意")); + + + lvbuTodos = flowRecordRepository.findTodoByOperatorId(lvBu.getUserId(), pageRequest).getContent(); + assertEquals(1, lvbuTodos.size()); + + // 提交部门经理 吕布 的审批 + lvbuTodo = lvbuTodos.get(0); + flowService.submitFlow(lvbuTodo.getId(), lvBu, leave, Opinion.pass("吕布同意")); + + + zhaoYunTodos = flowRecordRepository.findTodoByOperatorId(zhaoYun.getUserId(), pageRequest).getContent(); + assertEquals(1, zhaoYunTodos.size()); + + // 提交部门经理 赵云 的审批 + zhaoYunTodo = zhaoYunTodos.get(0); + flowService.submitFlow(zhaoYunTodo.getId(), zhaoYun, leave, Opinion.pass("赵云同意")); + + caocaoTodos = flowRecordRepository.findTodoByOperatorId(caocao.getUserId(), pageRequest).getContent(); + assertEquals(1, caocaoTodos.size()); + + // 提交部门经理 曹操 的审批 + caocaoTodo = caocaoTodos.get(0); + flowService.submitFlow(caocaoTodo.getId(), caocao, leave, Opinion.pass("曹操同意")); + + + bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent();; + assertEquals(12, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(12, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(13, snapshots.size()); + + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/user/User.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/user/User.java new file mode 100644 index 00000000..5a260782 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/user/User.java @@ -0,0 +1,46 @@ +package com.codingapi.springboot.flow.user; + +import lombok.Getter; +import lombok.Setter; + +@Getter +public class User implements IFlowOperator{ + + @Setter + private long id; + + private String name; + + private boolean isFlowManager; + + private User entrustOperator; + + public User(String name,boolean isFlowManager) { + this.name = name; + this.isFlowManager = isFlowManager; + } + + public User(String name,User entrustOperator) { + this.name = name; + this.entrustOperator = entrustOperator; + } + + public User(String name) { + this(name,false); + } + + @Override + public IFlowOperator entrustOperator() { + return entrustOperator; + } + + @Override + public long getUserId() { + return id; + } + + @Override + public boolean isFlowManager() { + return isFlowManager; + } +} diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 11b91dab..70d558a1 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.8.11 + 2.9.0 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index ca03cc75..167d7c35 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.8.11 + 2.9.0 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/ApplicationHandlerUtils.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/ApplicationHandlerUtils.java index 0e97bc4a..61c7a932 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/ApplicationHandlerUtils.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/ApplicationHandlerUtils.java @@ -2,9 +2,12 @@ import com.codingapi.springboot.framework.exception.EventException; import com.codingapi.springboot.framework.exception.EventLoopException; +import org.springframework.core.ResolvableType; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.stream.Collectors; class ApplicationHandlerUtils implements IHandler { @@ -40,18 +43,37 @@ public void addHandler(IHandler handler) { } } + /** + * 获取订阅的事件类型 + */ + private Class getHandlerEventClass(IHandler handler) { + ResolvableType resolvableType = ResolvableType.forClass(handler.getClass()).as(IHandler.class); + return resolvableType.getGeneric(0).resolve(); + } + @Override public void handler(IEvent event) { Class eventClass = event.getClass(); + + List> matchHandlers = handlers + .stream() + .filter(handler -> { + Class targetClass = getHandlerEventClass(handler); + return targetClass.isAssignableFrom(eventClass); + }) + .sorted(Comparator.comparingInt(IHandler::order)) + .collect(Collectors.toList()); + + if (matchHandlers.isEmpty()) { + return; + } + List errorStack = new ArrayList<>(); - boolean throwException = false; - for (IHandler handler : handlers) { + boolean hasThrowException = false; + for (IHandler handler : matchHandlers) { try { - Class targetClass = handler.getHandlerEventClass(); - if (eventClass.equals(targetClass)) { - handler.handler(event); - } + handler.handler(event); } catch (Exception e) { if (e instanceof EventLoopException) { throw e; @@ -60,12 +82,12 @@ public void handler(IEvent event) { handler.error(e); errorStack.add(e); } catch (Exception err) { - throwException = true; + hasThrowException = true; errorStack.add(err); } } } - if(throwException){ + if (hasThrowException) { throw new EventException(errorStack); } } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/IEvent.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/IEvent.java index 327ca279..4cc12388 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/IEvent.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/IEvent.java @@ -12,5 +12,4 @@ */ public interface IEvent extends Serializable { - } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/IHandler.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/IHandler.java index 226ee17d..46248d8c 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/IHandler.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/IHandler.java @@ -1,7 +1,5 @@ package com.codingapi.springboot.framework.event; -import org.springframework.core.ResolvableType; - /** * handler 订阅 * @@ -9,6 +7,14 @@ */ public interface IHandler { + /** + * 事件订阅排序 + * 在同样的事件中,可以通过order来控制订阅的顺序 + */ + default int order() { + return 0; + } + /** * 订阅触发 * @@ -27,13 +33,6 @@ default void error(Exception exception) throws Exception { } - /** - * 获取订阅的事件类型 - */ - default Class getHandlerEventClass() { - ResolvableType resolvableType = ResolvableType.forClass(getClass()).as(IHandler.class); - return resolvableType.getGeneric(0).resolve(); - } } From 98c8e1d67ddf4a8e10e2128f8276788e924f23ca Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sat, 9 Nov 2024 10:57:41 +0800 Subject: [PATCH 054/101] update readme --- README.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6b653203..68fea27b 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,14 @@ ## Project Version | 项目版本说明 -v.2.x 为springboot 2.x版本,使用jdk8版本 -v.3.x 为springboot 3.x版本,使用jdk17版本 +v.2.x 为springboot 2.x版本,使用jdk8版本 +v.3.x 为springboot 3.x版本,使用jdk17版本 ## Project Modules Description | 项目模块介绍 * springboot-starter | Springboot领域驱动框架 * springboot-starter-data-fast | 快速数据呈现框架 +* springboot-starter-flow | 流程引擎框架 * springboot-starter-security | security权限框架支持基于JWT的无状态权限认证与Redis的有状态权限认证 ## SpringBoot DDD Architecture | SpringBoot DDD 框架图 @@ -41,6 +42,13 @@ v.3.x 为springboot 3.x版本,使用jdk17版本 ${last.version} + + + com.codingapi.springboot + springboot-starter-flow + ${last.version} + + com.codingapi.springboot @@ -63,7 +71,7 @@ https://github.com/codingapi/springboot-framework/wiki ## Example -见 [springboot-example](https://github.com/codingapi/springboot-example) +见 [springboot-example](https://github.com/codingapi/springboot-example) ## Reference Documentation From f39dfce1888ab41cc4ab350216fa7d3e1ce3810b Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sat, 9 Nov 2024 15:25:51 +0800 Subject: [PATCH 055/101] support loop event pusher --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../framework/event/DomainEventContext.java | 14 ++++++---- .../framework/event/EventPusher.java | 18 ++++++++++++- .../framework/event/EventTraceContext.java | 26 +++++++++++++++++++ 8 files changed, 57 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index ceb40070..18f5d46e 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.9.0 + 2.9.1 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 012a2759..f6b82eca 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.0 + 2.9.1 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index c2d61cc1..c5bcd2f6 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.0 + 2.9.1 springboot-starter-flow diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 70d558a1..5fcce435 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.0 + 2.9.1 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 167d7c35..7291503a 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.0 + 2.9.1 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/DomainEventContext.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/DomainEventContext.java index a22799e2..7d10e38c 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/DomainEventContext.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/DomainEventContext.java @@ -23,9 +23,13 @@ public static DomainEventContext getInstance() { return instance; } - private void push(IEvent event, boolean sync) { + private void push(IEvent event, boolean sync,boolean hasLoopEvent) { if (context != null) { String traceId = EventTraceContext.getInstance().getOrCreateTrace(); + if(hasLoopEvent){ + EventTraceContext.getInstance().clearTrace(); + traceId = EventTraceContext.getInstance().getOrCreateTrace(); + } EventTraceContext.getInstance().addEvent(traceId,event); context.publishEvent(new DomainEvent(event, sync,traceId)); } @@ -36,13 +40,13 @@ private void push(IEvent event, boolean sync) { * @see EventPusher * 默认 同步事件 */ - public void push(IEvent event) { + public void push(IEvent event,boolean hasLoopEvent) { if (event instanceof IAsyncEvent) { - this.push(event, false); + this.push(event, false,hasLoopEvent); } else if (event instanceof ISyncEvent) { - this.push(event, true); + this.push(event, true,hasLoopEvent); } else { - this.push(event, true); + this.push(event, true,hasLoopEvent); } } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/EventPusher.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/EventPusher.java index ee90ddd8..333569ea 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/EventPusher.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/EventPusher.java @@ -5,7 +5,23 @@ */ public class EventPusher { + /** + * 推送事件 + * 默认将自动检测事件是否有循环事件,当出现循环事件时,系统将会抛出循环调用异常。 + * @param event 事件 + */ public static void push(IEvent event) { - DomainEventContext.getInstance().push(event); + push(event, false); + } + + /** + * 推送事件 + * 默认将自动检测事件是否有循环事件,当出现循环事件时,系统将会抛出循环调用异常。 + * 设置hasLoopEvent为true,将不会检测循环事件。 + * @param event 事件 + * @param hasLoopEvent 是否有循环事件 + */ + public static void push(IEvent event, boolean hasLoopEvent) { + DomainEventContext.getInstance().push(event, hasLoopEvent); } } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/EventTraceContext.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/EventTraceContext.java index 844d2cf9..562e2768 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/EventTraceContext.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/event/EventTraceContext.java @@ -45,12 +45,19 @@ public String getEventKey() { return threadLocal.get(); } + /** + * create event key + * @param traceId traceId + */ void createEventKey(String traceId) { String eventKey = traceId + "#" + RandomGenerator.randomString(8); eventKeyState.put(eventKey, false); threadLocal.set(eventKey); } + /** + * check event state + */ void checkEventState() { String eventKey = threadLocal.get(); if (eventKey != null) { @@ -66,6 +73,11 @@ void checkEventState() { threadLocal.remove(); } + /** + * add event + * @param traceId traceId + * @param event event + */ void addEvent(String traceId, IEvent event) { boolean hasEventLoop = EventStackContext.getInstance().checkEventLoop(traceId, event); if (hasEventLoop) { @@ -78,4 +90,18 @@ void addEvent(String traceId, IEvent event) { } EventStackContext.getInstance().addEvent(traceId, event); } + + /** + * clear trace + */ + public void clearTrace() { + String eventKey = threadLocal.get(); + if (eventKey != null) { + String traceId = eventKey.split("#")[0]; + traceKeys.remove(traceId); + EventStackContext.getInstance().remove(traceId); + eventKeyState.remove(eventKey); + threadLocal.remove(); + } + } } From 510e84a86391f88f4f38648bde7364aae409b90c Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sat, 9 Nov 2024 16:11:49 +0800 Subject: [PATCH 056/101] add FlowService push loop event --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../springboot/flow/service/FlowService.java | 18 +++++++++--------- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 18f5d46e..593898fb 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.9.1 + 2.9.2 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index f6b82eca..773213a8 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.1 + 2.9.2 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index c5bcd2f6..92e666e4 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.1 + 2.9.2 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java index 25309652..a38930a6 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java @@ -99,7 +99,7 @@ public void urge(long recordId, IFlowOperator currentOperator) { // 推送催办消息 for (FlowRecord record : todoRecords) { IFlowOperator pushOperator = record.getCurrentOperator(); - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_URGE, record, pushOperator, flowWork, null)); + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_URGE, record, pushOperator, flowWork, null),true); } } @@ -222,10 +222,10 @@ public void transfer(long recordId, IFlowOperator currentOperator, IFlowOperator flowRecordRepository.save(Collections.singletonList(transferRecord)); // 推送转办消息 - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TRANSFER, flowRecord, currentOperator, flowWork, snapshot.toBindData())); + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TRANSFER, flowRecord, currentOperator, flowWork, snapshot.toBindData()),true); // 推送待办消息 - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, transferRecord, targetOperator, flowWork, snapshot.toBindData())); + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, transferRecord, targetOperator, flowWork, snapshot.toBindData()),true); } @@ -329,8 +329,8 @@ public FlowResult startFlow(String workCode, IFlowOperator operator, IBindData b // 推送事件消息 for (FlowRecord record : records) { - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_CREATE, record, operator, flowWork, snapshot.toBindData())); - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, record, operator, flowWork, snapshot.toBindData())); + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_CREATE, record, operator, flowWork, snapshot.toBindData()),true); + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, record, operator, flowWork, snapshot.toBindData()),true); } // 当前的审批记录 return new FlowResult(flowWork, records); @@ -428,7 +428,7 @@ public FlowResult submitFlow(long recordId, IFlowOperator currentOperator, IBind flowRecordRepository.update(flowRecord); flowRecordRepository.finishFlowRecordByProcessId(flowRecord.getProcessId()); - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_FINISH, flowRecord, currentOperator, flowWork, snapshot.toBindData())); + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_FINISH, flowRecord, currentOperator, flowWork, snapshot.toBindData()),true); return new FlowResult(flowWork, flowRecord); } @@ -467,12 +467,12 @@ public FlowResult submitFlow(long recordId, IFlowOperator currentOperator, IBind // 推送审批事件消息 int eventState = flowSourceDirection == FlowSourceDirection.PASS ? FlowApprovalEvent.STATE_PASS : FlowApprovalEvent.STATE_REJECT; - EventPusher.push(new FlowApprovalEvent(eventState, flowRecord, currentOperator, flowWork, snapshot.toBindData())); + EventPusher.push(new FlowApprovalEvent(eventState, flowRecord, currentOperator, flowWork, snapshot.toBindData()),true); // 推送待办事件消息 for (FlowRecord record : records) { IFlowOperator pushOperator = record.getCurrentOperator(); - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, record, pushOperator, flowWork, snapshot.toBindData())); + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, record, pushOperator, flowWork, snapshot.toBindData()),true); } return new FlowResult(flowWork, records); @@ -516,7 +516,7 @@ public void recall(long recordId, IFlowOperator currentOperator) { flowRecordRepository.update(flowRecord); flowRecordRepository.delete(childrenRecords); - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_RECALL, flowRecord, currentOperator, flowWork, null)); + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_RECALL, flowRecord, currentOperator, flowWork, null),true); } } diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 5fcce435..684ba48a 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.1 + 2.9.2 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 7291503a..c2a21b2c 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.1 + 2.9.2 springboot-starter From 5cf9a37b837490aa58e42d8bfb7980b4006c83a6 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sat, 9 Nov 2024 20:40:37 +0800 Subject: [PATCH 057/101] add event error logs --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../springboot/framework/exception/EventException.java | 3 +++ 6 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 593898fb..de1eedf5 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.9.2 + 2.9.3.1 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 773213a8..5ea6f609 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.2 + 2.9.3.1 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 92e666e4..8c7a9b08 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.2 + 2.9.3.1 springboot-starter-flow diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 684ba48a..9c55e0dd 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.2 + 2.9.3.1 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index c2a21b2c..d583065a 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.2 + 2.9.3.1 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/exception/EventException.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/exception/EventException.java index 68462e73..5a506fec 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/exception/EventException.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/exception/EventException.java @@ -13,5 +13,8 @@ public class EventException extends RuntimeException { public EventException(List error) { super(error.stream().map(Exception::getMessage).collect(Collectors.joining("\n"))); this.error = error; + for (Exception e : error) { + e.printStackTrace(); + } } } From 70241e9d939cec8b65645b3f990841b3b0cac3a2 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Tue, 12 Nov 2024 21:24:44 +0800 Subject: [PATCH 058/101] SecurityLoginHandler add UserDetails --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- .../springboot/security/AutoConfiguration.java | 2 +- .../security/filter/MyLoginFilter.java | 17 ++++++++++++++--- .../security/filter/SecurityLoginHandler.java | 3 ++- springboot-starter/pom.xml | 2 +- 8 files changed, 22 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index de1eedf5..247866dc 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.9.3.1 + 2.9.4 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 5ea6f609..1e5d7744 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.3.1 + 2.9.4 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 8c7a9b08..0581c28f 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.3.1 + 2.9.4 springboot-starter-flow diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 9c55e0dd..557f7bc5 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.3.1 + 2.9.4 springboot-starter-security diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java index 339ae029..b60f597c 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java @@ -78,7 +78,7 @@ public void preHandle(HttpServletRequest request, HttpServletResponse response, } @Override - public LoginResponse postHandle(HttpServletRequest request, HttpServletResponse response, LoginRequest loginRequest, Token token) { + public LoginResponse postHandle(HttpServletRequest request, HttpServletResponse response, LoginRequest loginRequest,UserDetails userDetails, Token token) { LoginResponse loginResponse = new LoginResponse(); loginResponse.setUsername(token.getUsername()); loginResponse.setToken(token.getToken()); diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java index 4f2939be..c0ecc62b 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java @@ -18,7 +18,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @@ -69,16 +69,21 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { log.debug("login success authentication ~"); - User user = (User) authResult.getPrincipal(); + UserDetails user = (UserDetails) authResult.getPrincipal(); LoginRequest loginRequest = LoginRequestContext.getInstance().get(); Token token = tokenGateway.create(user.getUsername(), loginRequest.getPassword(), user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()), TokenContext.getExtra()); - LoginResponse loginResponse = loginHandler.postHandle(request, response, loginRequest, token); + LoginResponse loginResponse = loginHandler.postHandle(request, response, loginRequest, user, token); String content = JSONObject.toJSONString(SingleResponse.of(loginResponse)); + + // 设置响应的 Content-Type 为 JSON,并指定字符编码为 UTF-8 + response.setContentType("application/json;charset=UTF-8"); + response.setCharacterEncoding("UTF-8"); + IOUtils.write(content, response.getOutputStream(), StandardCharsets.UTF_8); LoginRequestContext.getInstance().clean(); @@ -89,7 +94,13 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { log.debug("login fail authentication ~"); String content = JSONObject.toJSONString(Response.buildFailure("login.error", failed.getMessage())); + + // 设置响应的 Content-Type 为 JSON,并指定字符编码为 UTF-8 + response.setContentType("application/json;charset=UTF-8"); + response.setCharacterEncoding("UTF-8"); + IOUtils.write(content, response.getOutputStream(), StandardCharsets.UTF_8); + LoginRequestContext.getInstance().clean(); } } diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java index ed0b5876..db7bc687 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/SecurityLoginHandler.java @@ -3,6 +3,7 @@ import com.codingapi.springboot.security.dto.request.LoginRequest; import com.codingapi.springboot.security.dto.response.LoginResponse; import com.codingapi.springboot.security.gateway.Token; +import org.springframework.security.core.userdetails.UserDetails; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -12,5 +13,5 @@ public interface SecurityLoginHandler { void preHandle(HttpServletRequest request, HttpServletResponse response, LoginRequest loginRequest) throws Exception; - LoginResponse postHandle(HttpServletRequest request, HttpServletResponse response, LoginRequest loginRequest, Token token); + LoginResponse postHandle(HttpServletRequest request, HttpServletResponse response, LoginRequest loginRequest, UserDetails userDetails, Token token); } diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index d583065a..f0894754 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.3.1 + 2.9.4 springboot-starter From 5fcb38ed771295b21c6cb94a9c74137deb0b84a1 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Tue, 12 Nov 2024 21:28:14 +0800 Subject: [PATCH 059/101] SecurityLoginHandler add UserDetails --- .../springboot/security/filter/MyAccessDeniedHandler.java | 4 ++++ .../springboot/security/filter/MyAuthenticationFilter.java | 4 ++++ .../springboot/security/filter/MyLogoutSuccessHandler.java | 4 ++++ .../security/filter/MyUnAuthenticationEntryPoint.java | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAccessDeniedHandler.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAccessDeniedHandler.java index 4387a65b..beb79203 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAccessDeniedHandler.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAccessDeniedHandler.java @@ -20,6 +20,10 @@ public class MyAccessDeniedHandler implements AccessDeniedHandler { public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { log.debug("access denied"); String content = JSONObject.toJSONString(Response.buildFailure("not.access", "please check user authentication.")); + // 设置响应的 Content-Type 为 JSON,并指定字符编码为 UTF-8 + response.setContentType("application/json;charset=UTF-8"); + response.setCharacterEncoding("UTF-8"); + IOUtils.write(content, response.getOutputStream(), StandardCharsets.UTF_8); } } diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java index ed73501b..232fd938 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyAuthenticationFilter.java @@ -75,6 +75,10 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse private void writeResponse(HttpServletResponse servletResponse, Response returnResponse) throws IOException { String content = JSONObject.toJSONString(returnResponse); + // 设置响应的 Content-Type 为 JSON,并指定字符编码为 UTF-8 + servletResponse.setContentType("application/json;charset=UTF-8"); + servletResponse.setCharacterEncoding("UTF-8"); + IOUtils.write(content, servletResponse.getOutputStream(), StandardCharsets.UTF_8); } diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLogoutSuccessHandler.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLogoutSuccessHandler.java index 7ed35833..5b6520a6 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLogoutSuccessHandler.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLogoutSuccessHandler.java @@ -20,6 +20,10 @@ public class MyLogoutSuccessHandler implements LogoutSuccessHandler { public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { log.debug("logout success ~"); String content = JSONObject.toJSONString(Response.buildSuccess()); + // 设置响应的 Content-Type 为 JSON,并指定字符编码为 UTF-8 + response.setContentType("application/json;charset=UTF-8"); + response.setCharacterEncoding("UTF-8"); + IOUtils.write(content, response.getOutputStream(), StandardCharsets.UTF_8); } } diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyUnAuthenticationEntryPoint.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyUnAuthenticationEntryPoint.java index c1e610f8..885b6241 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyUnAuthenticationEntryPoint.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyUnAuthenticationEntryPoint.java @@ -21,6 +21,10 @@ public class MyUnAuthenticationEntryPoint implements AuthenticationEntryPoint { public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { log.debug("no authentication ~"); String content = JSONObject.toJSONString(Response.buildFailure("not.login", "please to login.")); + // 设置响应的 Content-Type 为 JSON,并指定字符编码为 UTF-8 + response.setContentType("application/json;charset=UTF-8"); + response.setCharacterEncoding("UTF-8"); + IOUtils.write(content, response.getOutputStream(), StandardCharsets.UTF_8); } } From ac75bb8c60e2d0f8d42d8fc13a6e5fbb7ce29451 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Wed, 13 Nov 2024 21:11:37 +0800 Subject: [PATCH 060/101] add SQLBuilder --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- .../springboot/fast/jdbc/JdbcQuery.java | 29 ++++++++-- .../springboot/fast/jpa/JPAQuery.java | 13 ++++- .../springboot/fast/jpa/SQLBuilder.java | 55 +++++++++++++++++++ .../repository/DynamicNativeRepository.java | 27 +++++++-- .../jpa/repository/DynamicRepository.java | 11 +++- .../springboot/fast/DemoRepositoryTest.java | 47 ++++++++++++++-- springboot-starter-flow/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- 11 files changed, 167 insertions(+), 25 deletions(-) create mode 100644 springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/SQLBuilder.java diff --git a/pom.xml b/pom.xml index 247866dc..89c2678d 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.9.4 + 2.9.5 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 1e5d7744..ae8091cb 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.4 + 2.9.5 4.0.0 diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQuery.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQuery.java index d20e379c..0a3e8be0 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQuery.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jdbc/JdbcQuery.java @@ -1,5 +1,6 @@ package com.codingapi.springboot.fast.jdbc; +import com.codingapi.springboot.fast.jpa.SQLBuilder; import org.apache.commons.text.CaseUtils; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -37,14 +38,26 @@ public Map mapRow(ResultSet rs, int rowNum) throws SQLException } } - public List> queryForList(String sql, Object... params) { + public List> queryForMapList(SQLBuilder builder) { + return queryForMapList(builder.getSQL(), builder.getParams()); + } + + public List> queryForMapList(String sql, Object... params) { return jdbcTemplate.query(sql, params, new CamelCaseRowMapper()); } + public List queryForList(SQLBuilder builder) { + return (List) queryForList(builder.getSQL(), builder.getClazz(), builder.getParams()); + } + public List queryForList(String sql, Class clazz, Object... params) { return jdbcTemplate.query(sql, params, new BeanPropertyRowMapper<>(clazz)); } + public Page queryForPage(SQLBuilder builder, PageRequest pageRequest) { + return (Page)queryForPage(builder.getSQL(), builder.getCountSQL(), builder.getClazz(), pageRequest, builder.getParams()); + } + public Page queryForPage(String sql, String countSql, Class clazz, PageRequest pageRequest, Object... params) { List list = jdbcTemplate.query(sql, params, new BeanPropertyRowMapper<>(clazz)); long count = this.countQuery(countSql, params); @@ -52,19 +65,23 @@ public Page queryForPage(String sql, String countSql, Class clazz, Pag } public Page queryForPage(String sql, Class clazz, PageRequest pageRequest, Object... params) { - String countSql = "select count(1) "+sql; + String countSql = "select count(1) " + sql; return this.queryForPage(sql, countSql, clazz, pageRequest, params); } - public Page> queryForPage(String sql, String countSql, PageRequest pageRequest, Object... params) { + public Page> queryForMapPage(SQLBuilder builder, PageRequest pageRequest) { + return queryForMapPage(builder.getSQL(), builder.getCountSQL(), pageRequest, builder.getParams()); + } + + public Page> queryForMapPage(String sql, String countSql, PageRequest pageRequest, Object... params) { List> list = jdbcTemplate.query(sql, params, new CamelCaseRowMapper()); long count = this.countQuery(countSql, params); return new PageImpl<>(list, pageRequest, count); } - public Page> queryForPage(String sql, PageRequest pageRequest, Object... params) { - String countSql = "select count(1) "+sql; - return this.queryForPage(sql, countSql, pageRequest, params); + public Page> queryForMapPage(String sql, PageRequest pageRequest, Object... params) { + String countSql = "select count(1) " + sql; + return this.queryForMapPage(sql, countSql, pageRequest, params); } diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQuery.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQuery.java index 6342af68..aa1ddaff 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQuery.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/JPAQuery.java @@ -1,12 +1,12 @@ package com.codingapi.springboot.fast.jpa; -import javax.persistence.EntityManager; -import javax.persistence.TypedQuery; import lombok.AllArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; import java.util.List; @AllArgsConstructor @@ -14,6 +14,10 @@ public class JPAQuery { private final EntityManager entityManager; + public List listQuery(SQLBuilder builder) { + return listQuery(builder.getClazz(),builder.getSQL(),builder.getParams()); + } + public List listQuery(Class clazz, String sql, Object... params) { TypedQuery query = entityManager.createQuery(sql, clazz); if (params != null) { @@ -24,6 +28,11 @@ public List listQuery(Class clazz, String sql, Object... params) { return query.getResultList(); } + public Page pageQuery(SQLBuilder builder,PageRequest pageRequest) { + return pageQuery(builder.getClazz(), builder.getSQL(), builder.getCountSQL(),pageRequest,builder.getParams()); + } + + public Page pageQuery(Class clazz, String sql, PageRequest pageRequest, Object... params) { return pageQuery(clazz,sql,"select count(1) " + sql,pageRequest,params); } diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/SQLBuilder.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/SQLBuilder.java new file mode 100644 index 00000000..0eaaf2e4 --- /dev/null +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/SQLBuilder.java @@ -0,0 +1,55 @@ +package com.codingapi.springboot.fast.jpa; + +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +public class SQLBuilder { + + private final StringBuilder sqlBuilder; + private final StringBuilder countSQLBuilder; + private int index; + private final List params; + @Getter + private final Class clazz; + + public SQLBuilder(String sql) { + this(null, sql, "select count(1) from " + sql); + } + + public SQLBuilder(Class clazz, String sql) { + this(clazz, sql, "select count(1) from " + sql); + } + + public SQLBuilder(Class clazz, String sql, String countSQL) { + this.countSQLBuilder = new StringBuilder(countSQL); + this.sqlBuilder = new StringBuilder(sql); + this.index = 1; + this.params = new ArrayList<>(); + this.clazz = clazz; + } + + public void append(String sql, Object value) { + if (value != null) { + sqlBuilder.append(" ").append(sql).append(index).append(" "); + countSQLBuilder.append(" ").append(sql).append(index).append(" "); + params.add(value); + index++; + } + } + + public String getSQL() { + return sqlBuilder.toString(); + } + + public String getCountSQL() { + return countSQLBuilder.toString(); + } + + public Object[] getParams() { + return params.toArray(); + } + + +} diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicNativeRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicNativeRepository.java index 1dd846b9..7641a488 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicNativeRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicNativeRepository.java @@ -1,6 +1,7 @@ package com.codingapi.springboot.fast.jpa.repository; import com.codingapi.springboot.fast.jdbc.JdbcQueryContext; +import com.codingapi.springboot.fast.jpa.SQLBuilder; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.repository.NoRepositoryBean; @@ -11,8 +12,12 @@ @NoRepositoryBean public interface DynamicNativeRepository extends BaseRepository { + default List> dynamicNativeListMapQuery(SQLBuilder builder) { + return JdbcQueryContext.getInstance().getJdbcQuery().queryForMapList(builder); + } + default List> dynamicNativeListMapQuery(String sql, Object... params) { - return JdbcQueryContext.getInstance().getJdbcQuery().queryForList(sql, params); + return JdbcQueryContext.getInstance().getJdbcQuery().queryForMapList(sql, params); } default List dynamicNativeListQuery(String sql, Object... params) { @@ -23,6 +28,10 @@ default List dynamicNativeListQuery(Class clazz, String sql, Object... return JdbcQueryContext.getInstance().getJdbcQuery().queryForList(sql, clazz, params); } + default List dynamicNativeListQuery(SQLBuilder sqlBuilder) { + return JdbcQueryContext.getInstance().getJdbcQuery().queryForList(sqlBuilder); + } + default Page dynamicNativePageQuery(String sql, String countSql, PageRequest request, Object... params) { return dynamicNativePageQuery(getEntityClass(), sql, countSql, request, params); } @@ -39,12 +48,20 @@ default Page dynamicNativePageQuery(Class clazz, String sql, PageReque return JdbcQueryContext.getInstance().getJdbcQuery().queryForPage(sql, clazz, request, params); } - default Page> dynamicNativePageMapQuery(String sql, String countSql, PageRequest request, Object... params) { - return JdbcQueryContext.getInstance().getJdbcQuery().queryForPage(sql, countSql, request, params); + default Page dynamicNativePageQuery(SQLBuilder sqlBuilder, PageRequest request) { + return JdbcQueryContext.getInstance().getJdbcQuery().queryForPage(sqlBuilder, request); + } + + default Page> dynamicNativeMapPageMapQuery(SQLBuilder sqlBuilder,PageRequest request) { + return JdbcQueryContext.getInstance().getJdbcQuery().queryForMapPage(sqlBuilder,request); + } + + default Page> dynamicNativeMapPageMapQuery(String sql, String countSql, PageRequest request, Object... params) { + return JdbcQueryContext.getInstance().getJdbcQuery().queryForMapPage(sql, countSql, request, params); } - default Page> dynamicNativePageMapQuery(String sql, PageRequest request, Object... params) { - return JdbcQueryContext.getInstance().getJdbcQuery().queryForPage(sql, request, params); + default Page> dynamicNativeMapPageMapQuery(String sql, PageRequest request, Object... params) { + return JdbcQueryContext.getInstance().getJdbcQuery().queryForMapPage(sql, request, params); } } diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRepository.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRepository.java index b8e0850c..a2fe6c76 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRepository.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicRepository.java @@ -1,6 +1,7 @@ package com.codingapi.springboot.fast.jpa.repository; import com.codingapi.springboot.fast.jpa.JpaQueryContext; +import com.codingapi.springboot.fast.jpa.SQLBuilder; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.repository.NoRepositoryBean; @@ -11,6 +12,10 @@ @SuppressWarnings("unchecked") public interface DynamicRepository extends BaseRepository { + default List dynamicListQuery(SQLBuilder builder) { + return (List) JpaQueryContext.getInstance().getJPAQuery().listQuery(builder); + } + default List dynamicListQuery(String sql, Object... params) { return (List) JpaQueryContext.getInstance().getJPAQuery().listQuery(getEntityClass(), sql, params); } @@ -19,12 +24,16 @@ default List dynamicListQuery(Class clazz, String sql, Object... param return (List) JpaQueryContext.getInstance().getJPAQuery().listQuery(clazz, sql, params); } + default Page dynamicPageQuery(SQLBuilder builder, PageRequest request) { + return (Page) JpaQueryContext.getInstance().getJPAQuery().pageQuery(builder, request); + } + default Page dynamicPageQuery(String sql, String countSql, PageRequest request, Object... params) { return (Page) JpaQueryContext.getInstance().getJPAQuery().pageQuery(getEntityClass(), sql, countSql, request, params); } default Page dynamicPageQuery(String sql, PageRequest request, Object... params) { - return (Page) JpaQueryContext.getInstance().getJPAQuery().pageQuery(getEntityClass(), sql, request, params); + return (Page) JpaQueryContext.getInstance().getJPAQuery().pageQuery(getEntityClass(), sql, request, params); } default Page dynamicPageQuery(Class clazz, String sql, String countSql, PageRequest request, Object... params) { diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java index 9b2ec82a..cb2ea09b 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java @@ -1,6 +1,7 @@ package com.codingapi.springboot.fast; import com.codingapi.springboot.fast.entity.Demo; +import com.codingapi.springboot.fast.jpa.SQLBuilder; import com.codingapi.springboot.fast.repository.DemoRepository; import com.codingapi.springboot.framework.dto.request.Filter; import com.codingapi.springboot.framework.dto.request.PageRequest; @@ -47,7 +48,7 @@ void findAll() { demoRepository.save(demo2); PageRequest request = new PageRequest(); - request.setCurrent(0); + request.setCurrent(1); request.setPageSize(10); request.addFilter("name", "123"); @@ -68,7 +69,7 @@ void pageRequest() { demoRepository.save(demo2); PageRequest request = new PageRequest(); - request.setCurrent(0); + request.setCurrent(1); request.setPageSize(10); request.addFilter("name", Relation.LIKE, "%2%"); @@ -89,7 +90,7 @@ void customInSearch() { demoRepository.save(demo2); PageRequest request = new PageRequest(); - request.setCurrent(0); + request.setCurrent(1); request.setPageSize(10); request.addFilter("id", Relation.IN, 1, 2, 3); @@ -116,7 +117,12 @@ void customOrSearch() { request.setPageSize(10); - request.orFilters(Filter.as("id", Relation.IN, 1, 2, 3), Filter.as("name", "123")); +// request.andFilter(Filter.as("id", Relation.IN, 1, 2, 3), Filter.as("name", "123")); + request.addFilter("name", "456").orFilters(Filter.as("id", Relation.IN, 1, 2, 3), Filter.as("name", "123")); + + request.addSort(Sort.by("id").descending()); + + Page page = demoRepository.pageRequest(request); log.info("demo:{}", page.getContent()); @@ -134,7 +140,32 @@ void dynamicListQuery() { demo2.setName("456"); demoRepository.save(demo2); - List list = demoRepository.dynamicListQuery("from Demo where name = ?1", "123"); + SQLBuilder builder = new SQLBuilder(Demo.class,"from Demo where 1=1"); + String search = "12"; + builder.append("and name like ?","%"+search+"%"); + + List list = demoRepository.dynamicListQuery(builder); + assertEquals(1, list.size()); + } + + + + @Test + void dynamicNativeListQuery() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demo2.setName("456"); + demoRepository.save(demo2); + + SQLBuilder builder = new SQLBuilder(Demo.class,"select * from t_demo where 1=1"); + String search = "12"; + builder.append("and name like ?","%"+search+"%"); + + List list = demoRepository.dynamicNativeListQuery(builder); assertEquals(1, list.size()); } @@ -150,7 +181,11 @@ void dynamicPageQuery() { demo2.setName("456"); demoRepository.save(demo2); - Page page = demoRepository.dynamicPageQuery("from Demo where name = ?1", PageRequest.of(1, 2), "123"); + SQLBuilder builder = new SQLBuilder(Demo.class,"select d from Demo d where 1=1","select count(1) from Demo d where 1=1"); + String search = "12"; + builder.append("and d.name like ?","%"+search+"%"); + + Page page = demoRepository.dynamicPageQuery(builder,PageRequest.of(1, 2)); assertEquals(1, page.getTotalElements()); } diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 0581c28f..a08e272c 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.4 + 2.9.5 springboot-starter-flow diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 557f7bc5..f65d1323 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.4 + 2.9.5 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index f0894754..8fd9e8f8 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.4 + 2.9.5 springboot-starter From 7bf1a12fe9b418616c7306a2a431d91e3a519e45 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Fri, 15 Nov 2024 09:51:39 +0800 Subject: [PATCH 061/101] add FlowRecord workCode --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../springboot/flow/domain/FlowNode.java | 3 ++ .../flow/query/FlowRecordQuery.java | 42 +++++++++++++++ .../springboot/flow/record/FlowRecord.java | 7 +++ .../service/FlowRecordBuilderService.java | 6 +-- .../springboot/flow/service/FlowService.java | 1 + .../repository/FlowRecordRepositoryImpl.java | 52 +++++++++++++++++++ springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- 11 files changed, 113 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 89c2678d..f67cfd10 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.9.5 + 2.9.6 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index ae8091cb..79dbc8cb 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.5 + 2.9.6 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index a08e272c..cbd1aee6 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.5 + 2.9.6 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java index 7d9291ed..2084a4f6 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java @@ -179,6 +179,7 @@ public List loadFlowNodeOperator(FlowSession flowSessio * 创建流程记录 * * @param workId 流程设计id + * @param workCode 流程设计编码 * @param processId 流程id * @param preId 上一条流程记录id * @param title 流程标题 @@ -188,6 +189,7 @@ public List loadFlowNodeOperator(FlowSession flowSessio * @return 流程记录 */ public FlowRecord createRecord(long workId, + String workCode, String processId, long preId, String title, @@ -207,6 +209,7 @@ public FlowRecord createRecord(long workId, record.setNodeCode(this.code); record.setCreateTime(System.currentTimeMillis()); record.setWorkId(workId); + record.setWorkCode(workCode); record.setFlowStatus(FlowStatus.RUNNING); record.setPostponedCount(0); record.setCreateOperator(createOperator); diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/query/FlowRecordQuery.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/query/FlowRecordQuery.java index de2e4298..79e372dc 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/query/FlowRecordQuery.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/query/FlowRecordQuery.java @@ -21,6 +21,16 @@ public interface FlowRecordQuery { Page findTodoByOperatorId(long operatorId, PageRequest pageRequest); + /** + * 查看个人的待办数据(指定流程) + * + * @param operatorId 操作人 + * @param workCode 流程编码 + * @return 流程记录 + */ + Page findTodoByOperatorId(long operatorId,String workCode, PageRequest pageRequest); + + /** * 查看个人的已办数据 * @param operatorId 操作人 @@ -29,6 +39,14 @@ public interface FlowRecordQuery { Page findDoneByOperatorId(long operatorId, PageRequest pageRequest); + /** + * 查看个人的已办数据 (指定流程) + * @param operatorId 操作人 + * @param workCode 流程编码 + * @return 流程记录 + */ + Page findDoneByOperatorId(long operatorId,String workCode, PageRequest pageRequest); + /** * 查看个人的发起数据 (含待办与已办) * @param operatorId 操作人 @@ -37,6 +55,14 @@ public interface FlowRecordQuery { Page findInitiatedByOperatorId(long operatorId, PageRequest pageRequest); + /** + * 查看个人的发起数据 (含待办与已办、指定流程) + * @param operatorId 操作人 + * @param workCode 流程编码 + * @return 流程记录 + */ + Page findInitiatedByOperatorId(long operatorId,String workCode, PageRequest pageRequest); + /** * 查看个人的超时的待办流程 * @param operatorId 操作人 @@ -44,6 +70,13 @@ public interface FlowRecordQuery { */ Page findTimeoutTodoByOperatorId(long operatorId, PageRequest pageRequest); + /** + * 查看个人的超时的待办流程 (指定流程) + * @param operatorId 操作人 + * @param workCode 流程编码 + * @return 流程记录 + */ + Page findTimeoutTodoByOperatorId(long operatorId,String workCode, PageRequest pageRequest); /** * 查看个人的延期的待办流程 @@ -52,4 +85,13 @@ public interface FlowRecordQuery { */ Page findPostponedTodoByOperatorId(long operatorId, PageRequest pageRequest); + + /** + * 查看个人的延期的待办流程 + * @param operatorId 操作人 + * @param workCode 流程编码 + * @return 流程记录 + */ + Page findPostponedTodoByOperatorId(long operatorId,String workCode, PageRequest pageRequest); + } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java index 41a57d1b..fa96ce9b 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java @@ -33,6 +33,12 @@ public class FlowRecord { * 工作id */ private long workId; + + /** + * 流程编码 + */ + private String workCode; + /** * 流程id */ @@ -367,6 +373,7 @@ public FlowRecord copy() { record.setPostponedCount(this.postponedCount); record.setPreId(this.preId); record.setWorkId(this.workId); + record.setWorkCode(this.workCode); record.setProcessId(this.processId); record.setNodeCode(this.nodeCode); record.setTitle(this.title); diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordBuilderService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordBuilderService.java index 654ef87a..9d4f812a 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordBuilderService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordBuilderService.java @@ -120,7 +120,7 @@ private List errMatcher(FlowNode currentNode, IFlowOperator currentO for (IFlowOperator operator : operators) { FlowSession content = new FlowSession(flowWork, currentNode, createOperator, operator, snapshot.toBindData(), opinion, historyRecords); String recordTitle = currentNode.generateTitle(content); - FlowRecord record = currentNode.createRecord(flowWork.getId(), processId, preId, recordTitle, createOperator, operator, snapshot); + FlowRecord record = currentNode.createRecord(flowWork.getId(),flowWork.getCode(), processId, preId, recordTitle, createOperator, operator, snapshot); recordList.add(record); } return recordList; @@ -138,7 +138,7 @@ private List errMatcher(FlowNode currentNode, IFlowOperator currentO if (!matcherOperators.isEmpty()) { for (IFlowOperator matcherOperator : matcherOperators) { String recordTitle = node.generateTitle(content); - FlowRecord record = node.createRecord(flowWork.getId(), processId, preId, recordTitle, createOperator, matcherOperator, snapshot); + FlowRecord record = node.createRecord(flowWork.getId(),flowWork.getCode(), processId, preId, recordTitle, createOperator, matcherOperator, snapshot); recordList.add(record); } } @@ -182,7 +182,7 @@ public List createRecord(FlowNode currentNode, IFlowOperator current String recordTitle = currentNode.generateTitle(flowSession); recordList = new ArrayList<>(); for (IFlowOperator operator : operators) { - FlowRecord record = currentNode.createRecord(workId, processId, preId, recordTitle, createOperator, operator, snapshot); + FlowRecord record = currentNode.createRecord(workId,flowWork.getCode(), processId, preId, recordTitle, createOperator, operator, snapshot); recordList.add(record); } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java index a38930a6..6bb0d65d 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java @@ -20,6 +20,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java index 5875e22d..36cfde5c 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java @@ -43,6 +43,8 @@ public List findFlowRecordByPreId(long preId) { return cache.stream().filter(record -> record.getPreId() == preId).collect(Collectors.toList()); } + + @Override public List findFlowRecordByProcessId(String processId) { return cache.stream().filter(record -> record.getProcessId().equals(processId)) @@ -59,24 +61,52 @@ public Page findAll(PageRequest pageRequest) { return new PageImpl<>(cache); } + @Override public Page findDoneByOperatorId(long operatorId,PageRequest pageRequest) { List flowRecords = cache.stream().filter(record -> record.isDone() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList()); return new PageImpl<>(flowRecords); } + @Override + public Page findDoneByOperatorId(long operatorId, String workCode, PageRequest pageRequest) { + List flowRecords = cache.stream().filter(record -> record.isDone() + && record.getCurrentOperator().getUserId() == operatorId + && record.getWorkCode().equals(workCode) + ).collect(Collectors.toList()); + return new PageImpl<>(flowRecords); + } + @Override public Page findInitiatedByOperatorId(long operatorId,PageRequest pageRequest) { List flowRecords = cache.stream().filter(record -> record.isInitiated() && record.getCreateOperator().getUserId() == operatorId).collect(Collectors.toList()); return new PageImpl<>(flowRecords); } + + @Override + public Page findInitiatedByOperatorId(long operatorId, String workCode, PageRequest pageRequest) { + List flowRecords = cache.stream().filter( + record -> record.isInitiated() + && record.getCreateOperator().getUserId() == operatorId + && record.getWorkCode().equals(workCode) + ).collect(Collectors.toList()); + return new PageImpl<>(flowRecords); + } + + @Override public Page findTodoByOperatorId(long operatorId,PageRequest pageRequest) { List flowRecords = cache.stream().filter(record -> record.isTodo() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList()); return new PageImpl<>(flowRecords); } + @Override + public Page findTodoByOperatorId(long operatorId, String workCode, PageRequest pageRequest) { + List flowRecords = cache.stream().filter(record -> record.isTodo() && record.getCurrentOperator().getUserId() == operatorId && record.getWorkCode().equals(workCode)).collect(Collectors.toList()); + return new PageImpl<>(flowRecords); + } + @Override public Page findTimeoutTodoByOperatorId(long operatorId,PageRequest pageRequest) { List flowRecords = cache.stream().filter(record -> record.isTimeout() && record.isTodo() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList()); @@ -84,12 +114,34 @@ public Page findTimeoutTodoByOperatorId(long operatorId,PageRequest } + + @Override + public Page findTimeoutTodoByOperatorId(long operatorId, String workCode, PageRequest pageRequest) { + List flowRecords = cache.stream().filter( + record -> record.isTimeout() + && record.isTodo() && record.getCurrentOperator().getUserId() == operatorId + && record.getWorkCode().equals(workCode) + ).collect(Collectors.toList()); + return new PageImpl<>(flowRecords); + } + + @Override public Page findPostponedTodoByOperatorId(long operatorId,PageRequest pageRequest) { List flowRecords = cache.stream().filter(record -> record.isPostponed() && record.isTodo() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList()); return new PageImpl<>(flowRecords); } + + @Override + public Page findPostponedTodoByOperatorId(long operatorId, String workCode, PageRequest pageRequest) { + List flowRecords = cache.stream().filter(record -> record.isPostponed() + && record.isTodo() && record.getCurrentOperator().getUserId() == operatorId + && record.getWorkCode().equals(workCode) + ).collect(Collectors.toList()); + return new PageImpl<>(flowRecords); + } + @Override public void finishFlowRecordByProcessId(String processId) { cache.stream() diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index f65d1323..62c748bd 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.5 + 2.9.6 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 8fd9e8f8..e1440db2 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.5 + 2.9.6 springboot-starter From adcd6b8641f90929ff02c7a0a5e2bba2dbe0e928 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Thu, 5 Dec 2024 23:53:56 +0800 Subject: [PATCH 062/101] update flow --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- .../jpa/repository/DynamicSQLBuilder.java | 2 +- springboot-starter-flow/pom.xml | 2 +- .../springboot/flow/bind/IBindData.java | 9 + .../flow/build/FlowWorkBuilder.java | 43 +- .../springboot/flow/build/SchemaReader.java | 31 +- .../springboot/flow/content/FlowSession.java | 143 ++++- .../springboot/flow/domain/FlowButton.java | 78 +++ .../springboot/flow/domain/FlowNode.java | 34 +- .../springboot/flow/domain/FlowWork.java | 1 + .../springboot/flow/domain/Opinion.java | 32 ++ .../springboot/flow/em/ApprovalType.java | 6 +- .../springboot/flow/em/FlowButtonType.java | 29 + .../flow/em/FlowSourceDirection.java | 8 +- .../springboot/flow/em/FlowType.java | 4 + .../springboot/flow/em/NodeType.java | 4 + .../flow/event/FlowApprovalEvent.java | 2 + .../flow/matcher/OperatorMatcher.java | 1 - .../springboot/flow/pojo/FlowDetail.java | 28 +- .../flow/pojo/FlowSubmitResult.java | 23 + .../flow/query/FlowRecordQuery.java | 19 + .../springboot/flow/record/FlowRecord.java | 11 + .../repository/FlowProcessRepository.java | 2 +- .../flow/repository/FlowRecordRepository.java | 6 +- .../springboot/flow/result/MessageResult.java | 138 +++++ .../springboot/flow/result/ResultState.java | 23 + .../serializable/FlowNodeSerializable.java | 8 +- .../serializable/FlowWorkSerializable.java | 13 +- .../flow/service/FlowDirectionService.java | 4 +- ...ilderService.java => FlowNodeService.java} | 317 +++++++---- ...vice.java => FlowRecordVerifyService.java} | 14 +- .../springboot/flow/service/FlowService.java | 506 ++++-------------- .../service/impl/FlowCustomEventService.java | 76 +++ .../flow/service/impl/FlowDetailService.java | 114 ++++ .../service/impl/FlowPostponedService.java | 44 ++ .../flow/service/impl/FlowRecallService.java | 73 +++ .../flow/service/impl/FlowSaveService.java | 51 ++ .../flow/service/impl/FlowStartService.java | 142 +++++ .../flow/service/impl/FlowSubmitService.java | 196 +++++++ .../service/impl/FlowTransferService.java | 100 ++++ .../service/impl/FlowTrySubmitService.java | 250 +++++++++ .../flow/service/impl/FlowUrgeService.java | 50 ++ .../repository/FlowRecordRepositoryImpl.java | 26 +- .../springboot/flow/test/BuildTest.java | 9 +- .../springboot/flow/test/CirculateTest.java | 138 +++++ .../springboot/flow/test/ErrorTest.java | 26 +- .../springboot/flow/test/FlowTest.java | 156 ++++-- .../springboot/flow/test/FlowTest2.java | 7 +- .../flow/test/MultiRelationFlowTest.java | 36 +- .../springboot/flow/test/QueryTest.java | 71 +-- .../springboot/flow/test/ScriptBuildTest.java | 9 +- .../springboot/flow/test/ScriptTest.java | 2 +- .../springboot/flow/test/SignTest.java | 39 +- .../springboot/flow/test/TrySubmitTest.java | 134 +++++ springboot-starter-security/pom.xml | 2 +- .../security/filter/MyLoginFilter.java | 2 +- springboot-starter/pom.xml | 2 +- 58 files changed, 2555 insertions(+), 745 deletions(-) create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowButton.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowButtonType.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowSubmitResult.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/result/MessageResult.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/result/ResultState.java rename springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/{FlowRecordBuilderService.java => FlowNodeService.java} (53%) rename springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/{FlowRecordService.java => FlowRecordVerifyService.java} (91%) create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowCustomEventService.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowDetailService.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowPostponedService.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRecallService.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSaveService.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStartService.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTransferService.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTrySubmitService.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowUrgeService.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/CirculateTest.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/TrySubmitTest.java diff --git a/pom.xml b/pom.xml index f67cfd10..f9d62149 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.9.6 + 2.9.7 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 79dbc8cb..b2efbee5 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.6 + 2.9.7 4.0.0 diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java index caa1a9d7..eb2a15c8 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java @@ -45,7 +45,7 @@ public String getHQL() { Sort sort = request.getSort(); if (sort.isSorted()) { hql.append(" ORDER BY "); - List orders = sort.toList(); + List orders = sort.collect(Collectors.toList()); for (int i = 0; i < orders.size(); i++) { Sort.Order order = orders.get(i); hql.append(order.getProperty()).append(" ").append(order.getDirection().name()); diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index cbd1aee6..4aac3f3a 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.6 + 2.9.7 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/IBindData.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/IBindData.java index 6a7287bc..4814cdcb 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/IBindData.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/IBindData.java @@ -15,4 +15,13 @@ public interface IBindData { default String toJsonSnapshot() { return JSONObject.toJSONString(this); } + + + /** + * 获取类名称 + * @return 类名称 + */ + default String getClazzName() { + return this.getClass().getName(); + } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/FlowWorkBuilder.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/FlowWorkBuilder.java index 08d89bf5..2749b7cf 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/FlowWorkBuilder.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/FlowWorkBuilder.java @@ -1,5 +1,6 @@ package com.codingapi.springboot.flow.build; +import com.codingapi.springboot.flow.domain.FlowButton; import com.codingapi.springboot.flow.domain.FlowNode; import com.codingapi.springboot.flow.domain.FlowRelation; import com.codingapi.springboot.flow.domain.FlowWork; @@ -12,6 +13,8 @@ import com.codingapi.springboot.flow.user.IFlowOperator; import com.codingapi.springboot.framework.utils.RandomGenerator; +import java.util.List; + /** * 流程工作构建器 */ @@ -65,25 +68,43 @@ public FlowWork build() { public class Nodes { - public Nodes node(String id,String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, long timeout, TitleGenerator titleGenerator, ErrTrigger errTrigger, boolean editable) { - FlowNode node = new FlowNode(id, name, code, view, NodeType.parser(code), approvalType, titleGenerator, operatorMatcher, timeout, errTrigger, editable); + public Nodes node(String id, String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, long timeout, TitleGenerator titleGenerator, ErrTrigger errTrigger, boolean editable, List buttons) { + FlowNode node = new FlowNode(id, name, code, view, NodeType.parser(code), approvalType, titleGenerator, operatorMatcher, timeout, errTrigger, editable, buttons); work.addNode(node); return this; } - public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher,long timeout, boolean editable) { - return node(RandomGenerator.generateUUID(),name, code, view, approvalType, operatorMatcher, timeout, TitleGenerator.defaultTitleGenerator(), null, editable); + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, long timeout, boolean editable) { + return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, timeout, TitleGenerator.defaultTitleGenerator(), null, editable, null); + } + + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, long timeout, boolean editable, List buttons) { + return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, timeout, TitleGenerator.defaultTitleGenerator(), null, editable, buttons); } + + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, boolean editable) { - return node(RandomGenerator.generateUUID(),name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), null, editable); + return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), null, editable, null); + } + + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, boolean editable, List buttons) { + return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), null, editable, buttons); + } + + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, List buttons) { + return node(name, code, view, approvalType, operatorMatcher, true, buttons); } public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher) { - return node(name, code, view, approvalType, operatorMatcher, true); + return node(name, code, view, approvalType, operatorMatcher, true, null); + } + + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, ErrTrigger errTrigger, boolean editable, List buttons) { + return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), errTrigger, editable, buttons); } - public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, ErrTrigger errTrigger, boolean editable) { - return node(RandomGenerator.generateUUID(),name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), errTrigger, editable); + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, ErrTrigger errTrigger, boolean editable) { + return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), errTrigger, editable, null); } @@ -102,13 +123,13 @@ public FlowWork build() { public class Relations { public Relations relation(String name, String source, String target) { - return relation(name,source,target,OutTrigger.defaultOutTrigger(),1,false); + return relation(name, source, target, OutTrigger.defaultOutTrigger(), 1, false); } - public Relations relation(String name, String source, String target, OutTrigger outTrigger,int order, boolean back) { + public Relations relation(String name, String source, String target, OutTrigger outTrigger, int order, boolean back) { FlowNode from = work.getNodeByCode(source); FlowNode to = work.getNodeByCode(target); - FlowRelation relation = new FlowRelation(RandomGenerator.generateUUID(), name, from, to, outTrigger,order, back); + FlowRelation relation = new FlowRelation(RandomGenerator.generateUUID(), name, from, to, outTrigger, order, back); work.addRelation(relation); return this; } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/SchemaReader.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/SchemaReader.java index bb5abd75..f169b75e 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/SchemaReader.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/SchemaReader.java @@ -2,6 +2,7 @@ import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; +import com.codingapi.springboot.flow.domain.FlowButton; import com.codingapi.springboot.flow.domain.FlowNode; import com.codingapi.springboot.flow.domain.FlowRelation; import com.codingapi.springboot.flow.em.ApprovalType; @@ -37,7 +38,7 @@ public SchemaReader(String schema) { } - private void loadNodes(){ + private void loadNodes() { JSONArray nodes = data.getJSONArray("nodes"); for (int i = 0; i < nodes.size(); i++) { JSONObject node = nodes.getJSONObject(i); @@ -53,39 +54,43 @@ private void loadNodes(){ int timeout = properties.getIntValue("timeout"); String errTrigger = properties.getString("errTrigger"); String id = properties.getString("id"); - FlowNode flowNode = new FlowNode(id,name,code,view, NodeType.parser(type),ApprovalType.parser(approvalType),new TitleGenerator(titleGenerator), - new OperatorMatcher(operatorMatcher),timeout, StringUtils.hasLength(errTrigger)?new ErrTrigger(errTrigger):null,editable); + List buttons = null; + if(properties.containsKey("buttons")){ + buttons = properties.getJSONArray("buttons").toJavaList(FlowButton.class); + } + FlowNode flowNode = new FlowNode(id, name, code, view, NodeType.parser(type), ApprovalType.parser(approvalType), new TitleGenerator(titleGenerator), + new OperatorMatcher(operatorMatcher), timeout, StringUtils.hasLength(errTrigger) ? new ErrTrigger(errTrigger) : null, editable, buttons); flowNodes.add(flowNode); } } - private FlowNode getFlowNodeById(String id){ - for(FlowNode flowNode:flowNodes){ - if(flowNode.getId().equals(id)){ + private FlowNode getFlowNodeById(String id) { + for (FlowNode flowNode : flowNodes) { + if (flowNode.getId().equals(id)) { return flowNode; } } return null; } - private void loadEdges(){ + private void loadEdges() { JSONArray edges = data.getJSONArray("edges"); - for(int i=0;i historyRecords; + // bean提供者 private final FlowSessionBeanProvider provider; - public FlowSession(FlowWork flowWork, FlowNode flowNode, IFlowOperator createOperator, IFlowOperator currentOperator, IBindData bindData, Opinion opinion, List historyRecords) { + public FlowSession(FlowRecord flowRecord, + FlowWork flowWork, + FlowNode flowNode, + IFlowOperator createOperator, + IFlowOperator currentOperator, + IBindData bindData, + Opinion opinion, + List historyRecords) { + this.flowRecord = flowRecord; this.flowWork = flowWork; this.flowNode = flowNode; this.createOperator = createOperator; @@ -73,5 +95,124 @@ public OperatorResult createOperatorErrTrigger(long... operatorIds) { return new OperatorResult(operatorIds); } + /** + * 创建流程提醒 + * + * @param title 提醒标题 + * @return 提醒对象 + */ + public MessageResult createMessageResult(String title, String resultState) { + return MessageResult.create(title, resultState); + } + + + /** + * 创建流程提醒 + * + * @param title 提醒标题 + * @return 提醒对象 + */ + public MessageResult createMessageResult(String title) { + return MessageResult.create(title); + } + + /** + * 创建流程提醒 + * + * @param title 提醒标题 + * @param closeable 是否可关闭流程 + * @return 提醒对象 + */ + public MessageResult createMessageResult(String title, String resultState, boolean closeable) { + return MessageResult.create(title, resultState, closeable); + } + + + /** + * 创建流程提醒 + * + * @param title 提醒标题 + * @param items 提醒内容 + * @param closeable 是否可关闭流程 + * @return 提醒对象 + */ + public MessageResult createMessageResult(String title, String resultState, List items, boolean closeable) { + return MessageResult.create(title, resultState, items, closeable); + } + + /** + * 提交流程 + */ + public MessageResult submitFlow() { + if (flowRecord == null) { + throw new IllegalArgumentException("flow record is null"); + } + FlowService flowService = loadFlowService(); + FlowResult result = flowService.submitFlow(flowRecord.getId(), currentOperator, bindData, Opinion.pass(opinion.getAdvice())); + return MessageResult.create(result); + } + /** + * 驳回流程 + */ + public MessageResult rejectFlow() { + if (flowRecord == null) { + throw new IllegalArgumentException("flow record is null"); + } + FlowService flowService = loadFlowService(); + FlowResult result = flowService.submitFlow(flowRecord.getId(), currentOperator, bindData, Opinion.reject(opinion.getAdvice())); + return MessageResult.create(result); + } + + + /** + * 预提交流程 + */ + public MessageResult trySubmitFlow() { + if (flowRecord == null) { + throw new IllegalArgumentException("flow record is null"); + } + FlowService flowService = loadFlowService(); + FlowSubmitResult result = flowService.trySubmitFlow(flowRecord.getId(), currentOperator, bindData, Opinion.pass(opinion.getAdvice())); + return MessageResult.create(result); + } + + /** + * 保存流程 + */ + public void saveFlow() { + if (flowRecord == null) { + throw new IllegalArgumentException("flow record is null"); + } + FlowService flowService = loadFlowService(); + flowService.save(flowRecord.getId(), currentOperator, bindData, opinion.getAdvice()); + } + + + /** + * 催办流程 + */ + public void urgeFlow() { + if (flowRecord == null) { + throw new IllegalArgumentException("flow record is null"); + } + FlowService flowService = loadFlowService(); + flowService.urge(flowRecord.getId(), currentOperator); + } + + /** + * 撤回流程 + */ + public void recallFlow() { + if (flowRecord == null) { + throw new IllegalArgumentException("flow record is null"); + } + FlowService flowService = loadFlowService(); + flowService.recall(flowRecord.getId(), currentOperator); + } + + + private FlowService loadFlowService() { + return (FlowService) getBean("flowService"); + } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowButton.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowButton.java new file mode 100644 index 00000000..6bd5c440 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowButton.java @@ -0,0 +1,78 @@ +package com.codingapi.springboot.flow.domain; + +import com.codingapi.springboot.flow.bind.IBindData; +import com.codingapi.springboot.flow.content.FlowSession; +import com.codingapi.springboot.flow.em.FlowButtonType; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.result.MessageResult; +import com.codingapi.springboot.flow.script.GroovyShellContext; +import com.codingapi.springboot.flow.user.IFlowOperator; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +/** + * 流程按钮 + */ +@Setter +@Getter +public class FlowButton { + + /** + * 编号 + */ + private String id; + /** + * 名称 + */ + private String name; + /** + * 样式 + */ + private String style; + /** + * 事件类型 + */ + private FlowButtonType type; + /** + * 自定义事件内容 (后端脚本) + */ + private String groovy; + /** + * 排序 + */ + private int order; + + public boolean hasGroovy() { + return groovy != null; + } + + /** + * 执行按钮事件 + * @param flowRecord 流程记录 + * @param flowNode 节点 + * @param flowWork 流程设计器 + * @param createOperator 创建者 + * @param currentOperator 当前操作者 + * @param bindData 绑定数据 + * @param opinion 意见 + * @param historyRecords 历史记录 + */ + public MessageResult run(FlowRecord flowRecord, + FlowNode flowNode, + FlowWork flowWork, + IFlowOperator createOperator, + IFlowOperator currentOperator, + IBindData bindData, + Opinion opinion, + List historyRecords) { + if (groovy != null) { + //执行脚本 + FlowSession session = new FlowSession(flowRecord, flowWork, flowNode, createOperator, currentOperator, bindData, opinion, historyRecords); + GroovyShellContext.ShellScript script = GroovyShellContext.getInstance().parse(groovy); + return (MessageResult) script.invokeMethod("run", session); + } + return null; + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java index 2084a4f6..28342d75 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java @@ -2,7 +2,10 @@ import com.codingapi.springboot.flow.bind.BindDataSnapshot; import com.codingapi.springboot.flow.content.FlowSession; -import com.codingapi.springboot.flow.em.*; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.em.FlowStatus; +import com.codingapi.springboot.flow.em.FlowType; +import com.codingapi.springboot.flow.em.NodeType; import com.codingapi.springboot.flow.error.ErrTrigger; import com.codingapi.springboot.flow.error.ErrorResult; import com.codingapi.springboot.flow.generator.TitleGenerator; @@ -93,6 +96,11 @@ public class FlowNode { @Setter private ErrTrigger errTrigger; + /** + * 流程节点按钮 + */ + private List buttons; + public void verify(){ if (this.titleGenerator == null) { @@ -132,7 +140,8 @@ public FlowNodeSerializable toSerializable() { this.createTime, this.updateTime, this.timeout, - this.errTrigger == null ? null : this.errTrigger.getScript() + this.errTrigger == null ? null : this.errTrigger.getScript(), + this.buttons ); } @@ -147,7 +156,8 @@ public FlowNode(String id, OperatorMatcher operatorMatcher, long timeout, ErrTrigger errTrigger, - boolean editable) { + boolean editable, + List buttons) { this.id = id; this.code = code; this.name = name; @@ -161,6 +171,7 @@ public FlowNode(String id, this.errTrigger = errTrigger; this.timeout = timeout; this.editable = editable; + this.buttons = buttons; } @@ -303,4 +314,21 @@ public boolean isOverNode() { public boolean isStartNode() { return CODE_START.equals(this.code); } + + /** + * 是否传阅节点 + */ + public boolean isCirculate() { + return approvalType == ApprovalType.CIRCULATE; + } + + + public FlowButton getButton(String buttonId) { + for (FlowButton button : buttons) { + if (button.getId().equals(buttonId)) { + return button; + } + } + return null; + } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowWork.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowWork.java index 377aedd2..ffb91d08 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowWork.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowWork.java @@ -227,6 +227,7 @@ public FlowWorkSerializable toSerializable() { updateTime, enable, postponedMax, + schema, nodes.stream().map(FlowNode::toSerializable).collect(Collectors.toCollection(ArrayList::new)), relations.stream().map(FlowRelation::toSerializable).collect(Collectors.toCollection(ArrayList::new))); } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/Opinion.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/Opinion.java index 3f3321ae..2c1dc8f0 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/Opinion.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/Opinion.java @@ -5,6 +5,9 @@ import lombok.Setter; import lombok.ToString; +import java.util.ArrayList; +import java.util.List; + /** * 审批意见 */ @@ -28,6 +31,8 @@ public class Opinion { public static final int RESULT_PASS = 2; // 审批结果 驳回 public static final int RESULT_REJECT = 3; + // 审批结果 抄送 + public static final int RESULT_CIRCULATE = 4; /** * 审批意见 @@ -42,12 +47,31 @@ public class Opinion { */ private int type; + /** + * 指定流程的操作者 + * operatorIds 为空时,表示不指定操作者,由流程配置的操作者匹配器决定 + */ + private List operatorIds; + public Opinion(String advice, int result, int type) { this.advice = advice; this.result = result; this.type = type; } + public Opinion specify(List operatorIds) { + this.operatorIds = operatorIds; + return this; + } + + public Opinion specify(long... operatorIds) { + List operatorIdList = new ArrayList<>(); + for (long operatorId : operatorIds) { + operatorIdList.add(operatorId); + } + return specify(operatorIdList); + } + public static Opinion save(String advice) { return new Opinion(advice, RESULT_SAVE, TYPE_DEFAULT); } @@ -68,6 +92,14 @@ public static Opinion unSignAutoSuccess() { return new Opinion("非会签自动审批", RESULT_PASS, TYPE_AUTO); } + public static Opinion circulate() { + return new Opinion("", RESULT_CIRCULATE, TYPE_AUTO); + } + + public boolean isCirculate() { + return result == RESULT_CIRCULATE; + } + public boolean isSuccess() { return result == RESULT_PASS; } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/ApprovalType.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/ApprovalType.java index 60747587..ee58a275 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/ApprovalType.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/ApprovalType.java @@ -12,7 +12,11 @@ public enum ApprovalType { /** * 非会签 */ - UN_SIGN; + UN_SIGN, + /** + * 传阅 + */ + CIRCULATE; public static ApprovalType parser(String approvalType) { diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowButtonType.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowButtonType.java new file mode 100644 index 00000000..0fc7c71e --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowButtonType.java @@ -0,0 +1,29 @@ +package com.codingapi.springboot.flow.em; + +public enum FlowButtonType { + + // 保存 + SAVE, + // 发起 + START, + // 提交 + SUBMIT, + // 预提交 + TRY_SUBMIT, + // 指定人员提交 + SPECIFY_SUBMIT, + // 驳回 + REJECT, + // 转办 + TRANSFER, + // 撤销 + RECALL, + // 延期 + POSTPONED, + // 催办 + URGE, + // 自定义 + CUSTOM, + // 前端 + VIEW, +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowSourceDirection.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowSourceDirection.java index deb82606..4963f388 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowSourceDirection.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowSourceDirection.java @@ -15,9 +15,13 @@ public enum FlowSourceDirection { */ REJECT, /** - * + * 转办 */ - TRANSFER; + TRANSFER, + /** + * 传阅 + */ + CIRCULATE; public static FlowSourceDirection parser(String type){ for(FlowSourceDirection flowSourceDirection :values()){ diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowType.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowType.java index abee3ff0..79b54d00 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowType.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowType.java @@ -13,6 +13,10 @@ public enum FlowType { * 已办 */ DONE, + /** + * 传阅 + */ + CIRCULATE, /** * 转办 */ diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/NodeType.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/NodeType.java index ac87e905..0386f857 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/NodeType.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/NodeType.java @@ -13,6 +13,10 @@ public enum NodeType { * 审批 */ APPROVAL, + /** + * 传阅 + */ + CIRCULATE, /** * 结束 */ diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/event/FlowApprovalEvent.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/event/FlowApprovalEvent.java index f005767e..ba6665ad 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/event/FlowApprovalEvent.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/event/FlowApprovalEvent.java @@ -32,6 +32,8 @@ public class FlowApprovalEvent implements ISyncEvent { public static final int STATE_TODO = 7; // 催办 public static final int STATE_URGE = 8; + // 抄送 + public static final int STATE_CIRCULATE = 9; private final int state; diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/matcher/OperatorMatcher.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/matcher/OperatorMatcher.java index ae252f0b..950f950e 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/matcher/OperatorMatcher.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/matcher/OperatorMatcher.java @@ -88,7 +88,6 @@ public static OperatorMatcher specifyOperatorMatcher(long... userIds) { /** * 创建者操作者匹配器 * - * @param userIds 用户ids * @return 操作者匹配器 */ public static OperatorMatcher creatorOperatorMatcher() { diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java index f889f59f..a8f2be0f 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java @@ -45,12 +45,12 @@ public class FlowDetail { private final List operators; /** - * 创建者 + * 流程创建者 */ private final IFlowOperator flowCreator; /** - * 创建时间 + * 流程创建时间 */ private final long flowCreateTime; @@ -59,12 +59,18 @@ public class FlowDetail { */ private final List opinions; + /** + * 是否可以办理 + */ + private final boolean canHandle; + public FlowDetail(FlowRecord flowRecord, BindDataSnapshot snapshot, FlowWork flowWork, List historyRecords, - List operators) { + List operators, + boolean canHandle) { this.operators = operators; this.flowRecord = flowRecord; this.flowWork = flowWork; @@ -74,8 +80,24 @@ public FlowDetail(FlowRecord flowRecord, this.flowCreator = flowRecord.getCreateOperator(); this.flowCreateTime = flowRecord.getCreateTime(); this.flowNode = flowWork.getNodeByCode(flowRecord.getNodeCode()); + this.canHandle = canHandle; } + public FlowDetail(FlowWork flowWork, + FlowNode flowNode, + List operators, + boolean canHandle) { + this.flowWork = flowWork; + this.flowNode = flowNode; + this.operators = operators; + this.flowCreateTime = 0; + this.flowRecord = null; + this.historyRecords = null; + this.bindData = null; + this.opinions = null; + this.flowCreator = null; + this.canHandle = canHandle; + } @Getter public final class FlowOpinion { diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowSubmitResult.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowSubmitResult.java new file mode 100644 index 00000000..4bacea6e --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowSubmitResult.java @@ -0,0 +1,23 @@ +package com.codingapi.springboot.flow.pojo; + +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.user.IFlowOperator; +import lombok.Getter; + +import java.util.List; + +@Getter +public class FlowSubmitResult { + + private final FlowWork flowWork; + private final FlowNode flowNode; + private final List operators; + + public FlowSubmitResult(FlowWork flowWork, FlowNode flowNode, List operators) { + this.flowWork = flowWork; + this.flowNode = flowNode; + this.operators = operators; + } + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/query/FlowRecordQuery.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/query/FlowRecordQuery.java index 79e372dc..75a7a5cd 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/query/FlowRecordQuery.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/query/FlowRecordQuery.java @@ -12,6 +12,25 @@ public interface FlowRecordQuery { Page findAll(PageRequest pageRequest); + + /** + * 查看个人的未读与待办数据 + * + * @param operatorId 操作人 + * @return 流程记录 + */ + Page findUnReadByOperatorId(long operatorId, PageRequest pageRequest); + + + /** + * 查看个人的未读与待办数据(指定流程) + * + * @param operatorId 操作人 + * @param workCode 流程编码 + * @return 流程记录 + */ + Page findUnReadByOperatorId(long operatorId,String workCode, PageRequest pageRequest); + /** * 查看个人的待办数据 * diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java index fa96ce9b..81dbef11 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java @@ -230,6 +230,17 @@ public void submitRecord(IFlowOperator flowOperator, BindDataSnapshot snapshot, this.opinion = opinion; } + + /** + * 传阅流程 + */ + public void circulate() { + this.flowSourceDirection = FlowSourceDirection.CIRCULATE; + this.flowType = FlowType.CIRCULATE; + this.updateTime = System.currentTimeMillis(); + this.opinion = Opinion.circulate(); + } + /** * 转交流程 */ diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowProcessRepository.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowProcessRepository.java index 568d35b5..892141ea 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowProcessRepository.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowProcessRepository.java @@ -1,7 +1,7 @@ package com.codingapi.springboot.flow.repository; -import com.codingapi.springboot.flow.record.FlowProcess; import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.record.FlowProcess; /** * 流程仓库,每一个流程都会在创建时被创建一条process数据,用于标识流程 diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java index 623c420d..6948da3c 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java @@ -6,7 +6,7 @@ /** - * 流转记录数据仓库 + * 流转记录数据仓库 */ public interface FlowRecordRepository { @@ -43,6 +43,7 @@ public interface FlowRecordRepository { /** * 根据流程id查询流程记录 + * * @param processId 流程id */ List findFlowRecordByProcessId(String processId); @@ -50,6 +51,7 @@ public interface FlowRecordRepository { /** * 查询所有未完成的流程记录 + * * @param processId 流程id * @return List of FlowRecord */ @@ -64,7 +66,9 @@ public interface FlowRecordRepository { /** * 删除流程记录 + * * @param childrenRecords 流程记录 */ void delete(List childrenRecords); + } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/result/MessageResult.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/result/MessageResult.java new file mode 100644 index 00000000..5bedea04 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/result/MessageResult.java @@ -0,0 +1,138 @@ +package com.codingapi.springboot.flow.result; + +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.pojo.FlowResult; +import com.codingapi.springboot.flow.pojo.FlowSubmitResult; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.user.IFlowOperator; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Setter +@Getter +public class MessageResult { + + /** + * 展示的标题 + */ + private String title; + + /** + * 展示状态 + */ + private ResultState resultState; + + /** + * 展示的内容 + */ + private List items; + + /** + * 是否可关闭流程 + */ + private boolean closeable; + + /** + * 添加一个展示项 + * + * @param label 标签 + * @param value 值 + * @return this + */ + public MessageResult addItem(String label, String value) { + if (items == null) { + items = new java.util.ArrayList<>(); + } + items.add(new Message(label, value)); + return this; + } + + /** + * 是否可关闭流程 + * + * @param closeable 是否可关闭 + * @return this + */ + public MessageResult closeable(boolean closeable) { + this.closeable = closeable; + return this; + } + + /** + * 展示状态 + * + * @param resultState 展示状态 + * @return this + */ + public MessageResult resultState(String resultState) { + this.resultState = ResultState.parser(resultState); + return this; + } + + + @Setter + @Getter + @AllArgsConstructor + public static class Message { + private String label; + private String value; + } + + + + public static MessageResult create(FlowSubmitResult result) { + List operators = result.getOperators(); + FlowNode flowNode = result.getFlowNode(); + MessageResult messageResult = new MessageResult(); + messageResult.setResultState(ResultState.SUCCESS); + messageResult.setTitle("下级节点提示"); + messageResult.addItem("下级审批节点", flowNode.getName()); + StringBuilder usernames = new StringBuilder(); + for (IFlowOperator operator : operators) { + usernames.append(operator.getName()).append(","); + } + messageResult.addItem("下级审批人", usernames.toString()); + return messageResult; + } + + public static MessageResult create(FlowResult result) { + List records = result.getRecords(); + FlowWork flowWork = result.getFlowWork(); + MessageResult messageResult = new MessageResult(); + messageResult.setResultState(ResultState.SUCCESS); + messageResult.setTitle("流程审批完成"); + for (FlowRecord record : records) { + FlowNode flowNode = flowWork.getNodeByCode(record.getNodeCode()); + messageResult.addItem("下级审批节点", flowNode.getName()); + messageResult.addItem("下级审批人", record.getCurrentOperator().getName()); + } + return messageResult; + } + + + public static MessageResult create(String title) { + return create(title, "SUCCESS", null, false); + } + + public static MessageResult create(String title, String resultState) { + return create(title, resultState, null, false); + } + + + public static MessageResult create(String title, String resultState, boolean closeable) { + return create(title, resultState, null, closeable); + } + + public static MessageResult create(String title, String resultState, List items, boolean closeable) { + MessageResult messageResult = new MessageResult(); + messageResult.setTitle(title); + messageResult.setItems(items); + messageResult.setResultState(ResultState.parser(resultState)); + messageResult.setCloseable(closeable); + return messageResult; + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/result/ResultState.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/result/ResultState.java new file mode 100644 index 00000000..9048fd45 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/result/ResultState.java @@ -0,0 +1,23 @@ +package com.codingapi.springboot.flow.result; + +/** + * 状态数据 + */ +public enum ResultState { + SUCCESS, + INFO, + WARNING; + + + public static ResultState parser(String state) { + if ("SUCCESS".equalsIgnoreCase(state)) { + return SUCCESS; + } else if ("INFO".equalsIgnoreCase(state)) { + return INFO; + } else if ("WARNING".equalsIgnoreCase(state)) { + return WARNING; + } else { + return SUCCESS; + } + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowNodeSerializable.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowNodeSerializable.java index 5e22c61e..d6e80dea 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowNodeSerializable.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowNodeSerializable.java @@ -1,5 +1,6 @@ package com.codingapi.springboot.flow.serializable; +import com.codingapi.springboot.flow.domain.FlowButton; import com.codingapi.springboot.flow.domain.FlowNode; import com.codingapi.springboot.flow.em.ApprovalType; import com.codingapi.springboot.flow.em.NodeType; @@ -12,6 +13,7 @@ import lombok.Setter; import java.io.Serializable; +import java.util.List; /** * 流程节点序列化 @@ -86,9 +88,13 @@ public class FlowNodeSerializable implements Serializable { */ private String errTrigger; + /** + * 流程节点按钮 + */ + private List buttons; public FlowNode toFlowNode() { return new FlowNode(id, code, name, new TitleGenerator(titleGenerator), type, view, approvalType, - new OperatorMatcher(operatorMatcher), editable, createTime, updateTime, timeout, errTrigger == null ? null : new ErrTrigger(errTrigger)); + new OperatorMatcher(operatorMatcher), editable, createTime, updateTime, timeout, errTrigger == null ? null : new ErrTrigger(errTrigger),buttons); } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowWorkSerializable.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowWorkSerializable.java index 050561a1..1903d63d 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowWorkSerializable.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowWorkSerializable.java @@ -1,8 +1,10 @@ package com.codingapi.springboot.flow.serializable; +import com.codingapi.springboot.flow.domain.FlowButton; import com.codingapi.springboot.flow.domain.FlowNode; import com.codingapi.springboot.flow.domain.FlowWork; import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.em.FlowButtonType; import com.codingapi.springboot.flow.em.NodeType; import com.codingapi.springboot.flow.repository.FlowOperatorRepository; import com.esotericsoftware.kryo.Kryo; @@ -73,6 +75,11 @@ public class FlowWorkSerializable implements Serializable { @Setter private int postponedMax; + /** + * 流程的schema + */ + private String schema; + /** * 流程的节点(发起节点) */ @@ -97,6 +104,8 @@ public byte[] toSerializable() { kryo.register(FlowWorkSerializable.class); kryo.register(ApprovalType.class); kryo.register(NodeType.class); + kryo.register(FlowButton.class); + kryo.register(FlowButtonType.class); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); Output output = new Output(outputStream); @@ -114,6 +123,8 @@ public static FlowWorkSerializable fromSerializable(byte[] bytes) { kryo.register(FlowWorkSerializable.class); kryo.register(ApprovalType.class); kryo.register(NodeType.class); + kryo.register(FlowButton.class); + kryo.register(FlowButtonType.class); return kryo.readObject(new Input(bytes), FlowWorkSerializable.class); } @@ -132,7 +143,7 @@ public FlowWork toFlowWork(FlowOperatorRepository flowOperatorRepository) { postponedMax, flowNodes, relations.stream().map((item) -> item.toFlowRelation(flowNodes)).collect(Collectors.toList()), - null + schema ); } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java index 2326a8fd..3b8f3aac 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java @@ -12,7 +12,7 @@ /** * 流程方向服务 */ -class FlowDirectionService { +public class FlowDirectionService { private final FlowWork flowWork; private final FlowNode flowNode; @@ -112,7 +112,7 @@ public boolean isDefaultBackRecord() { /** * 判断当前流程是否为通过流程 */ - public boolean isPassBackRecord() { + public boolean isPassRecord() { return flowSourceDirection == FlowSourceDirection.PASS; } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordBuilderService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java similarity index 53% rename from springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordBuilderService.java rename to springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java index 9d4f812a..b7210a8d 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordBuilderService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java @@ -9,20 +9,26 @@ import com.codingapi.springboot.flow.error.ErrorResult; import com.codingapi.springboot.flow.error.NodeResult; import com.codingapi.springboot.flow.error.OperatorResult; +import com.codingapi.springboot.flow.event.FlowApprovalEvent; import com.codingapi.springboot.flow.record.FlowRecord; import com.codingapi.springboot.flow.repository.FlowOperatorRepository; import com.codingapi.springboot.flow.repository.FlowRecordRepository; import com.codingapi.springboot.flow.user.IFlowOperator; +import com.codingapi.springboot.framework.event.EventPusher; +import lombok.Getter; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** - * 流程记录构建服务 + * 流程节点服务 */ -class FlowRecordBuilderService { +public class FlowNodeService { + @Getter + private FlowNode nextNode; + private IFlowOperator nextOperator; private final FlowOperatorRepository flowOperatorRepository; private final FlowRecordRepository flowRecordRepository; @@ -31,6 +37,7 @@ class FlowRecordBuilderService { private final long preId; private final FlowWork flowWork; + private final FlowRecord flowRecord; private final Opinion opinion; private final IFlowOperator currentOperator; private final BindDataSnapshot snapshot; @@ -38,34 +45,99 @@ class FlowRecordBuilderService { private final IFlowOperator createOperator; - public FlowRecordBuilderService(FlowOperatorRepository flowOperatorRepository, - FlowRecordRepository flowRecordRepository, - BindDataSnapshot snapshot, - Opinion opinion, - IFlowOperator createOperator, - IFlowOperator currentOperator, - List historyRecords, - FlowWork flowWork, - String processId, - long preId) { + public FlowNodeService(FlowOperatorRepository flowOperatorRepository, + FlowRecordRepository flowRecordRepository, + BindDataSnapshot snapshot, + Opinion opinion, + IFlowOperator createOperator, + IFlowOperator currentOperator, + List historyRecords, + FlowWork flowWork, + FlowRecord flowRecord, + String processId, + long preId) { - if(createOperator==null){ - throw new IllegalArgumentException("createOperator is null"); - } - - this.createOperator = createOperator; this.flowOperatorRepository = flowOperatorRepository; - this.flowRecordRepository = flowRecordRepository; this.snapshot = snapshot; this.opinion = opinion; + this.createOperator = createOperator; this.currentOperator = currentOperator; + this.historyRecords = historyRecords; this.flowWork = flowWork; - - + this.flowRecord = flowRecord; this.processId = processId; this.preId = preId; - this.historyRecords = historyRecords; + } + + + /** + * 设置下一个节点 + */ + public void setNextNode(FlowNode nextNode) { + this.nextNode = nextNode; + this.nextOperator = currentOperator; + } + + + /** + * 加载下一个节点 + */ + public void loadNextPassNode(FlowNode currentNode) { + this.nextNode = matcherNextNode(currentNode, false); + this.nextOperator = currentOperator; + } + + /** + * 跳过传阅节点流转 + */ + public void skipCirculate() { + this.nextNode = matcherNextNode(nextNode, false); + this.nextOperator = currentOperator; + } + + /** + * 加载默认回退节点 + */ + public void loadDefaultBackNode(long parentRecordId) { + IFlowOperator flowOperator; + // 拒绝时,默认返回上一个已办节点 + FlowRecord preRecord = flowRecordRepository.getFlowRecordById(parentRecordId); + // 只寻找已办节点 + while (!preRecord.isDone()) { + // 继续寻找上一个节点 + preRecord = flowRecordRepository.getFlowRecordById(preRecord.getPreId()); + } + // 获取上一个节点的审批者,继续将审批者设置为当前审批者 + flowOperator = preRecord.getCurrentOperator(); + FlowNode nextNode = flowWork.getNodeByCode(preRecord.getNodeCode()); + if (nextNode == null) { + throw new IllegalArgumentException("next node not found"); + } + this.nextNode = nextNode; + this.nextOperator = flowOperator; + } + + + /** + * 加载自定义回退节点 + */ + public void loadCustomBackNode(FlowNode flowNode, long parentRecordId) { + FlowNode nextNode = this.matcherNextNode(flowNode, true); + if (nextNode == null) { + throw new IllegalArgumentException("next node not found"); + } + IFlowOperator flowOperator = currentOperator; + if (nextNode.isAnyOperatorMatcher()) { + // 如果是任意人员操作时则需要指定为当时审批人员为当前审批人员 + FlowRecord preFlowRecord = flowRecordRepository.getFlowRecordById(parentRecordId); + while (preFlowRecord.isTransfer() || !preFlowRecord.getNodeCode().equals(nextNode.getCode())) { + preFlowRecord = flowRecordRepository.getFlowRecordById(preFlowRecord.getPreId()); + } + flowOperator = preFlowRecord.getCurrentOperator(); + } + this.nextNode = nextNode; + this.nextOperator = flowOperator; } @@ -83,7 +155,7 @@ private FlowNode matcherNextNode(FlowNode flowNode, boolean back) { if (relations.isEmpty()) { throw new IllegalArgumentException("relation not found"); } - FlowSession flowSession = new FlowSession(flowWork, flowNode, createOperator, currentOperator, snapshot.toBindData(), opinion, historyRecords); + FlowSession flowSession = new FlowSession(flowRecord, flowWork, flowNode, createOperator, currentOperator, snapshot.toBindData(), opinion, historyRecords); List flowNodes = new ArrayList<>(); for (FlowRelation flowRelation : relations) { FlowNode node = flowRelation.trigger(flowSession); @@ -97,6 +169,101 @@ private FlowNode matcherNextNode(FlowNode flowNode, boolean back) { return flowNodes.get(0); } + + /** + * 创建流程记录 + * + * @return 流程记录 + */ + public List createRecord() { + // 创建下一节点的流程记录 + List records = this.createNextRecord(); + + // 检测流程是否为抄送节点 + while (this.nextNodeIsCirculate()) { + for (FlowRecord record : records) { + record.circulate(); + } + flowRecordRepository.save(records); + + for (FlowRecord record : records) { + IFlowOperator pushOperator = record.getCurrentOperator(); + + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_CIRCULATE, + record, + pushOperator, + flowWork, + snapshot.toBindData()), + true); + } + + this.skipCirculate(); + + records = this.createNextRecord(); + } + return records; + } + + + /** + * 加载下一节点的操作者 + * + * @return 操作者 + */ + public List loadNextNodeOperators() { + FlowSession flowSession = new FlowSession(flowRecord, flowWork, nextNode, createOperator, nextOperator, snapshot.toBindData(), opinion, historyRecords); + List operators = nextNode.loadFlowNodeOperator(flowSession, flowOperatorRepository); + if (operators.isEmpty()) { + if (nextNode.hasErrTrigger()) { + ErrorResult errorResult = nextNode.errMatcher(flowSession); + if (errorResult == null) { + throw new IllegalArgumentException("errMatcher match error."); + } + if (errorResult.isOperator()) { + List operatorIds = ((OperatorResult) errorResult).getOperatorIds(); + operators = flowOperatorRepository.findByIds(operatorIds); + } + } + } + return operators; + } + + + private List createNextRecord() { + FlowSession flowSession = new FlowSession(flowRecord, flowWork, + nextNode, + createOperator, + nextOperator, + snapshot.toBindData(), + opinion, + historyRecords); + + long workId = flowWork.getId(); + List operators = nextNode.loadFlowNodeOperator(flowSession, flowOperatorRepository); + List customOperatorIds = opinion.getOperatorIds(); + if (customOperatorIds != null && !customOperatorIds.isEmpty()) { + operators = operators.stream().filter(operator -> customOperatorIds.contains(operator.getUserId())).collect(Collectors.toList()); + if (operators.size() != customOperatorIds.size()) { + throw new IllegalArgumentException("operator not match."); + } + } + List recordList; + if (operators.isEmpty()) { + recordList = this.errMatcher(nextNode, nextOperator); + if (recordList.isEmpty()) { + throw new IllegalArgumentException("operator not match."); + } + } else { + String recordTitle = nextNode.generateTitle(flowSession); + recordList = new ArrayList<>(); + for (IFlowOperator operator : operators) { + FlowRecord record = nextNode.createRecord(workId, flowWork.getCode(), processId, preId, recordTitle, createOperator, operator, snapshot); + recordList.add(record); + } + } + return recordList; + } + /** * 异常匹配 * @@ -106,7 +273,7 @@ private FlowNode matcherNextNode(FlowNode flowNode, boolean back) { */ private List errMatcher(FlowNode currentNode, IFlowOperator currentOperator) { if (currentNode.hasErrTrigger()) { - FlowSession flowSession = new FlowSession(flowWork, currentNode, createOperator, currentOperator, snapshot.toBindData(), opinion, historyRecords); + FlowSession flowSession = new FlowSession(flowRecord, flowWork, currentNode, createOperator, currentOperator, snapshot.toBindData(), opinion, historyRecords); ErrorResult errorResult = currentNode.errMatcher(flowSession); if (errorResult == null) { throw new IllegalArgumentException("errMatcher match error."); @@ -118,9 +285,9 @@ private List errMatcher(FlowNode currentNode, IFlowOperator currentO List operatorIds = ((OperatorResult) errorResult).getOperatorIds(); List operators = flowOperatorRepository.findByIds(operatorIds); for (IFlowOperator operator : operators) { - FlowSession content = new FlowSession(flowWork, currentNode, createOperator, operator, snapshot.toBindData(), opinion, historyRecords); + FlowSession content = new FlowSession(flowRecord, flowWork, currentNode, createOperator, operator, snapshot.toBindData(), opinion, historyRecords); String recordTitle = currentNode.generateTitle(content); - FlowRecord record = currentNode.createRecord(flowWork.getId(),flowWork.getCode(), processId, preId, recordTitle, createOperator, operator, snapshot); + FlowRecord record = currentNode.createRecord(flowWork.getId(), flowWork.getCode(), processId, preId, recordTitle, createOperator, operator, snapshot); recordList.add(record); } return recordList; @@ -133,12 +300,12 @@ private List errMatcher(FlowNode currentNode, IFlowOperator currentO throw new IllegalArgumentException("node not found."); } List recordList = new ArrayList<>(); - FlowSession content = new FlowSession(flowWork, node, createOperator, currentOperator, snapshot.toBindData(), opinion, historyRecords); + FlowSession content = new FlowSession(flowRecord, flowWork, node, createOperator, currentOperator, snapshot.toBindData(), opinion, historyRecords); List matcherOperators = node.loadFlowNodeOperator(content, flowOperatorRepository); if (!matcherOperators.isEmpty()) { for (IFlowOperator matcherOperator : matcherOperators) { String recordTitle = node.generateTitle(content); - FlowRecord record = node.createRecord(flowWork.getId(),flowWork.getCode(), processId, preId, recordTitle, createOperator, matcherOperator, snapshot); + FlowRecord record = node.createRecord(flowWork.getId(), flowWork.getCode(), processId, preId, recordTitle, createOperator, matcherOperator, snapshot); recordList.add(record); } } @@ -151,101 +318,17 @@ private List errMatcher(FlowNode currentNode, IFlowOperator currentO /** - * 创建流程记录 - * - * @param currentNode 当前节点 - * @return 流程记录 - */ - public List createRecord(FlowNode currentNode, IFlowOperator currentOperator) { - return this.createRecord(currentNode,currentOperator,null); - } - - /** - * 创建流程记录 - * - * @param currentNode 当前节点 - * @param currentOperator 当前审批人 - * @param opinion 审批意见 - * @return 流程记录 - */ - public List createRecord(FlowNode currentNode, IFlowOperator currentOperator,Opinion opinion) { - FlowSession flowSession = new FlowSession(flowWork, currentNode, createOperator, currentOperator, snapshot.toBindData(), opinion, historyRecords); - long workId = flowWork.getId(); - List operators = currentNode.loadFlowNodeOperator(flowSession, flowOperatorRepository); - List recordList; - if (operators.isEmpty()) { - recordList= this.errMatcher(currentNode, currentOperator); - if (recordList.isEmpty()) { - throw new IllegalArgumentException("operator not match."); - } - } else { - String recordTitle = currentNode.generateTitle(flowSession); - recordList = new ArrayList<>(); - for (IFlowOperator operator : operators) { - FlowRecord record = currentNode.createRecord(workId,flowWork.getCode(), processId, preId, recordTitle, createOperator, operator, snapshot); - recordList.add(record); - } - } - if(!recordList.isEmpty()){ - for (FlowRecord record:recordList){ - if(opinion!=null){ - record.updateOpinion(opinion); - } - } - } - - return recordList; - } - - - /** - * 创建下一个节点 + * 下一节点的类型 */ - public List createNextRecord(FlowNode flowNode) { - FlowNode nextNode = this.matcherNextNode(flowNode, false); - return this.createRecord(nextNode, currentOperator); + public boolean nextNodeIsCirculate() { + return nextNode.isCirculate(); } - /** - * 创建自定义的下级别节点 - */ - public List createCustomBackRecord(FlowNode flowNode, long parentRecordId) { - FlowNode nextNode = this.matcherNextNode(flowNode, true); - if (nextNode == null) { - throw new IllegalArgumentException("next node not found"); - } - IFlowOperator flowOperator = currentOperator; - if (nextNode.isAnyOperatorMatcher()) { - // 如果是任意人员操作时则需要指定为当时审批人员为当前审批人员 - FlowRecord preFlowRecord = flowRecordRepository.getFlowRecordById(parentRecordId); - while (preFlowRecord.isTransfer() || !preFlowRecord.getNodeCode().equals(nextNode.getCode())) { - preFlowRecord = flowRecordRepository.getFlowRecordById(preFlowRecord.getPreId()); - } - flowOperator = preFlowRecord.getCurrentOperator(); - } - return this.createRecord(nextNode, flowOperator); - } /** - * 创建默认拒绝时的流程记录 + * 下一节点是否结束节点 */ - public List createDefaultBackRecord(long parentRecordId) { - IFlowOperator flowOperator; - // 拒绝时,默认返回上一个节点 - FlowRecord preRecord = flowRecordRepository.getFlowRecordById(parentRecordId); - // 去除所有的转办的记录 - while (preRecord.isTransfer()) { - // 继续寻找上一个节点 - preRecord = flowRecordRepository.getFlowRecordById(preRecord.getPreId()); - } - // 获取上一个节点的审批者,继续将审批者设置为当前审批者 - flowOperator = preRecord.getCurrentOperator(); - FlowNode nextNode = flowWork.getNodeByCode(preRecord.getNodeCode()); - if (nextNode == null) { - throw new IllegalArgumentException("next node not found"); - } - return this.createRecord(nextNode, flowOperator); + public boolean nextNodeIsOver() { + return nextNode.isOverNode(); } - - } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordVerifyService.java similarity index 91% rename from springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordService.java rename to springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordVerifyService.java index 910529bb..b923f10b 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordVerifyService.java @@ -13,7 +13,7 @@ /** * 流程记录服务(流程内部服务) */ -class FlowRecordService { +public class FlowRecordVerifyService { // constructor params private final long recordId; @@ -32,10 +32,10 @@ class FlowRecordService { @Getter private FlowRecord flowRecord; - public FlowRecordService(FlowRecordRepository flowRecordRepository, - FlowProcessRepository flowProcessRepository, - long recordId, - IFlowOperator currentOperator) { + public FlowRecordVerifyService(FlowRecordRepository flowRecordRepository, + FlowProcessRepository flowProcessRepository, + long recordId, + IFlowOperator currentOperator) { this.flowRecordRepository = flowRecordRepository; this.flowProcessRepository = flowProcessRepository; @@ -87,7 +87,9 @@ public void verifyFlowRecordIsDone() { */ public void verifyFlowRecordNotTodo() { if (flowRecord.isTodo()) { - throw new IllegalArgumentException("flow record is todo"); + if(!flowRecord.isStartRecord()) { + throw new IllegalArgumentException("flow record is todo"); + } } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java index 6bb0d65d..2de3d976 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java @@ -1,45 +1,89 @@ package com.codingapi.springboot.flow.service; -import com.codingapi.springboot.flow.bind.BindDataSnapshot; import com.codingapi.springboot.flow.bind.IBindData; -import com.codingapi.springboot.flow.content.FlowSession; -import com.codingapi.springboot.flow.domain.FlowNode; -import com.codingapi.springboot.flow.domain.FlowWork; import com.codingapi.springboot.flow.domain.Opinion; -import com.codingapi.springboot.flow.em.FlowSourceDirection; -import com.codingapi.springboot.flow.event.FlowApprovalEvent; import com.codingapi.springboot.flow.pojo.FlowDetail; import com.codingapi.springboot.flow.pojo.FlowResult; -import com.codingapi.springboot.flow.record.FlowBackup; -import com.codingapi.springboot.flow.record.FlowProcess; -import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.pojo.FlowSubmitResult; import com.codingapi.springboot.flow.repository.*; +import com.codingapi.springboot.flow.result.MessageResult; +import com.codingapi.springboot.flow.service.impl.*; import com.codingapi.springboot.flow.user.IFlowOperator; -import com.codingapi.springboot.framework.event.EventPusher; -import lombok.AllArgsConstructor; import org.springframework.transaction.annotation.Transactional; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; +import org.springframework.util.StringUtils; /** * 流程服务 */ @Transactional -@AllArgsConstructor public class FlowService { + private final FlowDetailService flowDetailService; + private final FlowStartService flowStartService; + private final FlowSubmitService flowSubmitService; + private final FlowCustomEventService flowCustomEventService; + private final FlowRecallService flowRecallService; + private final FlowTrySubmitService flowTrySubmitService; + private final FlowSaveService flowSaveService; + private final FlowTransferService flowTransferService; + private final FlowPostponedService flowPostponedService; + private final FlowUrgeService flowUrgeService; + + + public FlowService(FlowWorkRepository flowWorkRepository, + FlowRecordRepository flowRecordRepository, + FlowBindDataRepository flowBindDataRepository, + FlowOperatorRepository flowOperatorRepository, + FlowProcessRepository flowProcessRepository, + FlowBackupRepository flowBackupRepository) { + this.flowDetailService = new FlowDetailService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, flowOperatorRepository, flowProcessRepository); + this.flowStartService = new FlowStartService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, flowOperatorRepository, flowProcessRepository, flowBackupRepository); + this.flowSubmitService = new FlowSubmitService(flowRecordRepository, flowBindDataRepository, flowOperatorRepository, flowProcessRepository); + this.flowCustomEventService = new FlowCustomEventService(flowRecordRepository, flowProcessRepository); + this.flowRecallService = new FlowRecallService(flowRecordRepository, flowProcessRepository); + this.flowTrySubmitService = new FlowTrySubmitService(flowRecordRepository, flowBindDataRepository, flowOperatorRepository, flowProcessRepository, flowWorkRepository, flowBackupRepository); + this.flowSaveService = new FlowSaveService(flowRecordRepository, flowBindDataRepository, flowProcessRepository); + this.flowTransferService = new FlowTransferService(flowRecordRepository, flowBindDataRepository, flowProcessRepository); + this.flowPostponedService = new FlowPostponedService(flowRecordRepository, flowProcessRepository); + this.flowUrgeService = new FlowUrgeService(flowRecordRepository, flowProcessRepository); + } + + /** + * 流程详情 + * + * @param recordId 流程记录id + * @param workCode 流程编码 + * @return 流程详情 + */ + public FlowDetail detail(long recordId, String workCode, IFlowOperator currentOperator) { + if (StringUtils.hasText(workCode)) { + return flowDetailService.detail(workCode, currentOperator); + } else { + return flowDetailService.detail(recordId, currentOperator); + } + } + + /** + * 流程详情 + * + * @param recordId 流程记录id + * @return 流程详情 + */ + public FlowDetail detail(long recordId, IFlowOperator currentOperator) { + return this.detail(recordId, null, currentOperator); + } + - private final FlowWorkRepository flowWorkRepository; - private final FlowRecordRepository flowRecordRepository; - private final FlowBindDataRepository flowBindDataRepository; - private final FlowOperatorRepository flowOperatorRepository; - private final FlowProcessRepository flowProcessRepository; - private final FlowBackupRepository flowBackupRepository; + /** + * 流程详情 + * + * @param workCode 流程编号 + * @return 流程详情 + */ + public FlowDetail detail(String workCode, IFlowOperator currentOperator) { + return this.detail(0, workCode, currentOperator); + } /** @@ -49,7 +93,7 @@ public class FlowService { * @return 流程详情 */ public FlowDetail detail(long recordId) { - return detail(recordId, null); + return this.detail(recordId, null, null); } /** @@ -60,22 +104,7 @@ public FlowDetail detail(long recordId) { * @param time 延期时间 */ public void postponed(long recordId, IFlowOperator currentOperator, long time) { - FlowRecordService flowRecordService = new FlowRecordService(flowRecordRepository, - flowProcessRepository, - recordId, currentOperator); - - flowRecordService.loadFlowRecord(); - flowRecordService.verifyFlowRecordSubmitState(); - flowRecordService.verifyFlowRecordCurrentOperator(); - flowRecordService.loadFlowWork(); - flowRecordService.verifyFlowRecordNotFinish(); - flowRecordService.verifyFlowRecordNotDone(); - - FlowRecord flowRecord = flowRecordService.getFlowRecord(); - FlowWork flowWork = flowRecordService.getFlowWork(); - - flowRecord.postponedTime(flowWork.getPostponedMax(), time); - flowRecordRepository.update(flowRecord); + flowPostponedService.postponed(recordId, currentOperator, time); } /** @@ -85,63 +114,7 @@ public void postponed(long recordId, IFlowOperator currentOperator, long time) { * @param currentOperator 当前操作者 */ public void urge(long recordId, IFlowOperator currentOperator) { - FlowRecordService flowRecordService = new FlowRecordService(flowRecordRepository, - flowProcessRepository, - recordId, currentOperator); - flowRecordService.loadFlowRecord(); - flowRecordService.loadFlowWork(); - flowRecordService.verifyFlowRecordIsDone(); - - FlowRecord flowRecord = flowRecordService.getFlowRecord(); - FlowWork flowWork = flowRecordService.getFlowWork(); - - List todoRecords = flowRecordRepository.findTodoFlowRecordByProcessId(flowRecord.getProcessId()); - - // 推送催办消息 - for (FlowRecord record : todoRecords) { - IFlowOperator pushOperator = record.getCurrentOperator(); - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_URGE, record, pushOperator, flowWork, null),true); - } - - } - - /** - * 流程详情 - * 如果传递了currentOperator为流程的审批者时,在查看详情的时候可以将流程记录标记为已读 - * - * @param recordId 流程记录id - * @param currentOperator 当前操作者 - */ - public FlowDetail detail(long recordId, IFlowOperator currentOperator) { - - FlowRecordService flowRecordService = new FlowRecordService(flowRecordRepository, - flowProcessRepository, - recordId, currentOperator); - - flowRecordService.loadFlowRecord(); - flowRecordService.setFlowRecordRead(); - flowRecordService.loadFlowWork(); - - FlowRecord flowRecord = flowRecordService.getFlowRecord(); - FlowWork flowWork = flowRecordService.getFlowWork(); - - - BindDataSnapshot snapshot = flowBindDataRepository.getBindDataSnapshotById(flowRecord.getSnapshotId()); - List flowRecords = - flowRecordRepository.findFlowRecordByProcessId(flowRecord.getProcessId()). - stream(). - sorted((o1, o2) -> (int) (o2.getId() - o1.getId())) - .collect(Collectors.toList()); - - List operators = new ArrayList<>(); - // 获取所有的操作者 - for (FlowRecord record : flowRecords) { - operators.add(record.getCreateOperator()); - operators.add(record.getCurrentOperator()); - operators.add(record.getInterferedOperator()); - } - - return new FlowDetail(flowRecord, snapshot, flowWork, flowRecords, operators); + flowUrgeService.urge(recordId, currentOperator); } @@ -171,62 +144,7 @@ public FlowResult interfere(long recordId, IFlowOperator currentOperator, IBindD * @param advice 转办意见 */ public void transfer(long recordId, IFlowOperator currentOperator, IFlowOperator targetOperator, IBindData bindData, String advice) { - - FlowRecordService flowRecordService = new FlowRecordService(flowRecordRepository, - flowProcessRepository, - recordId, currentOperator); - - flowRecordService.loadFlowRecord(); - flowRecordService.verifyFlowRecordSubmitState(); - flowRecordService.verifyFlowRecordCurrentOperator(); - flowRecordService.verifyTargetOperatorIsNotCurrentOperator(targetOperator); - - flowRecordService.loadFlowWork(); - flowRecordService.loadFlowNode(); - - flowRecordService.verifyFlowRecordIsTodo(); - - FlowRecord flowRecord = flowRecordService.getFlowRecord(); - FlowWork flowWork = flowRecordService.getFlowWork(); - FlowNode flowNode = flowRecordService.getFlowNode(); - - - // 保存绑定数据 - BindDataSnapshot snapshot = new BindDataSnapshot(bindData); - flowBindDataRepository.save(snapshot); - - // 构建审批意见 - Opinion opinion = Opinion.transfer(advice); - - // 设置自己的流程状态为转办已完成 - flowRecord.transfer(currentOperator, snapshot, opinion); - flowRecordRepository.update(flowRecord); - - // 获取创建者 - IFlowOperator createOperator = flowRecord.getCreateOperator(); - - // 与当前流程同级的流程记录 - List historyRecords; - if (flowRecord.isStartRecord()) { - historyRecords = new ArrayList<>(); - } else { - historyRecords = flowRecordRepository.findFlowRecordByPreId(flowRecord.getPreId()); - } - - // 创建新的待办标题 - FlowSession content = new FlowSession(flowWork, flowNode, createOperator, targetOperator, snapshot.toBindData(), opinion, historyRecords); - String generateTitle = flowNode.generateTitle(content); - - // 创建转办记录 - FlowRecord transferRecord = flowRecord.copy(); - transferRecord.transferToTodo(generateTitle, targetOperator); - flowRecordRepository.save(Collections.singletonList(transferRecord)); - - // 推送转办消息 - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TRANSFER, flowRecord, currentOperator, flowWork, snapshot.toBindData()),true); - - // 推送待办消息 - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, transferRecord, targetOperator, flowWork, snapshot.toBindData()),true); + flowTransferService.transfer(recordId, currentOperator, targetOperator, bindData, advice); } @@ -239,24 +157,7 @@ public void transfer(long recordId, IFlowOperator currentOperator, IFlowOperator * @param advice 审批意见 */ public void save(long recordId, IFlowOperator currentOperator, IBindData bindData, String advice) { - FlowRecordService flowRecordService = new FlowRecordService(flowRecordRepository, - flowProcessRepository, - recordId, currentOperator); - flowRecordService.loadFlowRecord(); - flowRecordService.verifyFlowRecordSubmitState(); - flowRecordService.verifyFlowRecordCurrentOperator(); - flowRecordService.loadFlowWork(); - flowRecordService.loadFlowNode(); - flowRecordService.verifyFlowNodeEditableState(false); - - Opinion opinion = Opinion.save(advice); - FlowRecord flowRecord = flowRecordService.getFlowRecord(); - BindDataSnapshot snapshot = new BindDataSnapshot(flowRecord.getSnapshotId(), bindData); - flowBindDataRepository.update(snapshot); - - flowRecord.setOpinion(opinion); - flowRecordService.flowRecordRepository.update(flowRecord); - + flowSaveService.save(recordId, currentOperator, bindData, advice); } @@ -269,74 +170,36 @@ public void save(long recordId, IFlowOperator currentOperator, IBindData bindDat * @param advice 审批意见 */ public FlowResult startFlow(String workCode, IFlowOperator operator, IBindData bindData, String advice) { - // 检测流程是否存在 - FlowWork flowWork = flowWorkRepository.getFlowWorkByCode(workCode); - if (flowWork == null) { - throw new IllegalArgumentException("flow work not found"); - } - flowWork.verify(); - flowWork.enableValidate(); - - // 流程数据备份 - FlowBackup flowBackup = flowBackupRepository.getFlowBackupByWorkIdAndVersion(flowWork.getId(), flowWork.getUpdateTime()); - if (flowBackup == null) { - flowBackup = flowBackupRepository.backup(flowWork); - } - - // 保存流程 - FlowProcess flowProcess = new FlowProcess(flowBackup.getId(), operator); - flowProcessRepository.save(flowProcess); - - // 保存绑定数据 - BindDataSnapshot snapshot = new BindDataSnapshot(bindData); - flowBindDataRepository.save(snapshot); + return flowStartService.startFlow(workCode, operator, bindData, advice); + } - // 创建流程id - String processId = flowProcess.getProcessId(); - // 构建审批意见 - Opinion opinion = Opinion.pass(advice); + /** + * 尝试提交流程 (流程过程中) + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + * @param bindData 绑定数据 + * @param opinion 审批意见 + */ + public FlowSubmitResult trySubmitFlow(long recordId, IFlowOperator currentOperator, IBindData bindData, Opinion opinion) { + return flowTrySubmitService.trySubmitFlow(recordId, currentOperator, bindData, opinion); + } - // 获取开始节点 - FlowNode start = flowWork.getStartNode(); - if (start == null) { - throw new IllegalArgumentException("start node not found"); - } - // 设置开始流程的上一个流程id - long preId = 0; - - List historyRecords = new ArrayList<>(); - - FlowRecordBuilderService flowRecordBuilderService = new FlowRecordBuilderService( - flowOperatorRepository, - flowRecordRepository, - snapshot, - opinion, - operator, - operator, - historyRecords, - flowWork, - processId, - preId - ); - - // 创建待办记录 - List records = flowRecordBuilderService.createRecord(start, operator, opinion); - if (records.isEmpty()) { - throw new IllegalArgumentException("flow record not found"); - } - // 保存流程记录 - flowRecordRepository.save(records); - // 推送事件消息 - for (FlowRecord record : records) { - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_CREATE, record, operator, flowWork, snapshot.toBindData()),true); - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, record, operator, flowWork, snapshot.toBindData()),true); - } - // 当前的审批记录 - return new FlowResult(flowWork, records); + /** + * 尝试提交流程 (发起流程) + * + * @param workCode 流程编码 + * @param currentOperator 当前操作者 + * @param bindData 绑定数据 + * @param opinion 审批意见 + */ + public FlowSubmitResult trySubmitFlow(String workCode, IFlowOperator currentOperator, IBindData bindData, Opinion opinion) { + return flowTrySubmitService.trySubmitFlow(workCode, currentOperator, bindData, opinion); } + /** * 提交流程 * @@ -346,137 +209,21 @@ public FlowResult startFlow(String workCode, IFlowOperator operator, IBindData b * @param opinion 审批意见 */ public FlowResult submitFlow(long recordId, IFlowOperator currentOperator, IBindData bindData, Opinion opinion) { + return flowSubmitService.submitFlow(recordId, currentOperator, bindData, opinion); + } - FlowRecordService flowRecordService = new FlowRecordService(flowRecordRepository, flowProcessRepository, recordId, currentOperator); - - // 加载流程 - flowRecordService.loadFlowRecord(); - // 验证流程的提交状态 - flowRecordService.verifyFlowRecordSubmitState(); - // 验证当前操作者 - flowRecordService.verifyFlowRecordCurrentOperator(); - // 加载流程设计 - flowRecordService.loadFlowWork(); - // 加载流程节点 - flowRecordService.loadFlowNode(); - // 验证没有子流程 - flowRecordService.verifyChildrenRecordsIsEmpty(); - - // 获取流程记录对象 - FlowRecord flowRecord = flowRecordService.getFlowRecord(); - FlowNode flowNode = flowRecordService.getFlowNode(); - FlowWork flowWork = flowRecordService.getFlowWork(); - - - // 保存流程表单快照数据 - BindDataSnapshot snapshot = null; - if (flowNode.isEditable()) { - snapshot = new BindDataSnapshot(bindData); - flowBindDataRepository.save(snapshot); - } else { - snapshot = flowBindDataRepository.getBindDataSnapshotById(flowRecord.getSnapshotId()); - } - - // 审批方向判断服务 - FlowDirectionService flowDirectionService = new FlowDirectionService(flowRecordService.getFlowNode(), flowRecordService.getFlowWork(), opinion); - - // 加载流程审批方向 - flowDirectionService.loadFlowSourceDirection(); - // 验证审批方向 - flowDirectionService.verifyFlowSourceDirection(); - - // 根据当前方向提交流程 - FlowSourceDirection flowSourceDirection = flowDirectionService.getFlowSourceDirection(); - flowRecord.submitRecord(currentOperator, snapshot, opinion, flowSourceDirection); - flowRecordRepository.update(flowRecord); - - // 与当前流程同级的流程记录 - List historyRecords; - if (flowRecord.isStartRecord()) { - historyRecords = new ArrayList<>(); - } else { - historyRecords = flowRecordRepository.findFlowRecordByPreId(flowRecord.getPreId()); - } - flowDirectionService.bindHistoryRecords(historyRecords); - - // 判断流程是否结束(会签时需要所有人都通过) - if (flowNode.isSign()) { - boolean next = flowDirectionService.hasCurrentFlowNodeIsDone(); - if (next) { - List todoRecords = historyRecords.stream().filter(FlowRecord::isTodo).collect(Collectors.toList()); - return new FlowResult(flowWork, todoRecords); - } - } - - // 非会签下,当有人提交以后,将所有未提交的流程都自动提交,然后再执行下一节点 - if (flowNode.isUnSign()) { - for (FlowRecord record : historyRecords) { - if (record.isTodo() && record.getId() != flowRecord.getId()) { - record.autoPass(currentOperator, snapshot); - flowRecordRepository.update(flowRecord); - } - } - } - - // 根据所有提交意见,重新加载审批方向 - flowSourceDirection = flowDirectionService.reloadFlowSourceDirection(); - - - // 判断流程是否完成 - if (flowDirectionService.hasCurrentFlowIsFinish()) { - flowRecord.finish(); - flowRecord.submitRecord(currentOperator, snapshot, opinion, flowSourceDirection); - flowRecordRepository.update(flowRecord); - flowRecordRepository.finishFlowRecordByProcessId(flowRecord.getProcessId()); - - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_FINISH, flowRecord, currentOperator, flowWork, snapshot.toBindData()),true); - return new FlowResult(flowWork, flowRecord); - } - - // 获取流程的发起者 - IFlowOperator createOperator = flowRecord.getCreateOperator(); - - // 构建流程创建器 - FlowRecordBuilderService flowRecordBuilderService = new FlowRecordBuilderService( - flowOperatorRepository, - flowRecordRepository, - snapshot, - opinion, - createOperator, - currentOperator, - historyRecords, - flowWork, - flowRecord.getProcessId(), - flowRecord.getId() - ); - - // 创建下一节点的流程记录 - List records; - // 审批通过并进入下一节点 - if (flowDirectionService.isPassBackRecord()) { - records = flowRecordBuilderService.createNextRecord(flowNode); - // 审批拒绝返回上一节点 - } else if (flowDirectionService.isDefaultBackRecord()) { - records = flowRecordBuilderService.createDefaultBackRecord(flowRecord.getPreId()); - } else { - // 审批拒绝,并且自定了返回节点 - records = flowRecordBuilderService.createCustomBackRecord(flowNode, flowRecord.getPreId()); - } - - // 保存流程记录 - flowRecordRepository.save(records); - - // 推送审批事件消息 - int eventState = flowSourceDirection == FlowSourceDirection.PASS ? FlowApprovalEvent.STATE_PASS : FlowApprovalEvent.STATE_REJECT; - EventPusher.push(new FlowApprovalEvent(eventState, flowRecord, currentOperator, flowWork, snapshot.toBindData()),true); - - // 推送待办事件消息 - for (FlowRecord record : records) { - IFlowOperator pushOperator = record.getCurrentOperator(); - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, record, pushOperator, flowWork, snapshot.toBindData()),true); - } - return new FlowResult(flowWork, records); + /** + * 自定义事件 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + * @param buttonId 按钮id + * @param bindData 绑定数据 + * @param opinion 审批意见 + */ + public MessageResult customFlowEvent(long recordId, IFlowOperator currentOperator, String buttonId, IBindData bindData, Opinion opinion) { + return flowCustomEventService.customFlowEvent(recordId, currentOperator, buttonId, bindData, opinion); } @@ -487,37 +234,8 @@ public FlowResult submitFlow(long recordId, IFlowOperator currentOperator, IBind * @param currentOperator 当前操作者 */ public void recall(long recordId, IFlowOperator currentOperator) { - FlowRecordService flowRecordService = new FlowRecordService(flowRecordRepository, - flowProcessRepository, - recordId, currentOperator); - - flowRecordService.loadFlowRecord(); - flowRecordService.verifyFlowRecordCurrentOperator(); - flowRecordService.loadFlowWork(); - flowRecordService.loadFlowNode(); - flowRecordService.verifyFlowRecordNotFinish(); - flowRecordService.verifyFlowRecordNotTodo(); - - FlowRecord flowRecord = flowRecordService.getFlowRecord(); - FlowWork flowWork = flowRecordService.getFlowWork(); - - // 下一流程的流程记录 - List childrenRecords = flowRecordRepository.findFlowRecordByPreId(recordId); - // 下一流程均为办理且未读 - - if (childrenRecords.isEmpty()) { - throw new IllegalArgumentException("flow record not submit"); - } - - boolean allUnDone = childrenRecords.stream().allMatch(item -> item.isUnRead() && item.isTodo()); - if (!allUnDone) { - throw new IllegalArgumentException("flow record not recall"); - } - flowRecord.recall(); - flowRecordRepository.update(flowRecord); - - flowRecordRepository.delete(childrenRecords); - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_RECALL, flowRecord, currentOperator, flowWork, null),true); + flowRecallService.recall(recordId, currentOperator); } + } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowCustomEventService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowCustomEventService.java new file mode 100644 index 00000000..b7ab1dfc --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowCustomEventService.java @@ -0,0 +1,76 @@ +package com.codingapi.springboot.flow.service.impl; + +import com.codingapi.springboot.flow.bind.IBindData; +import com.codingapi.springboot.flow.domain.FlowButton; +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.FlowProcessRepository; +import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.result.MessageResult; +import com.codingapi.springboot.flow.service.FlowRecordVerifyService; +import com.codingapi.springboot.flow.user.IFlowOperator; +import lombok.AllArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Transactional +@AllArgsConstructor +public class FlowCustomEventService { + + private final FlowRecordRepository flowRecordRepository; + private final FlowProcessRepository flowProcessRepository; + + /** + * 自定义事件 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + * @param buttonId 按钮id + * @param bindData 绑定数据 + * @param opinion 审批意见 + */ + public MessageResult customFlowEvent(long recordId, IFlowOperator currentOperator, String buttonId, IBindData bindData, Opinion opinion) { + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, flowProcessRepository, recordId, currentOperator); + + // 加载流程 + flowRecordVerifyService.loadFlowRecord(); + // 验证流程的提交状态 + flowRecordVerifyService.verifyFlowRecordSubmitState(); + // 验证当前操作者 + flowRecordVerifyService.verifyFlowRecordCurrentOperator(); + // 加载流程设计 + flowRecordVerifyService.loadFlowWork(); + // 加载流程节点 + flowRecordVerifyService.loadFlowNode(); + // 验证没有子流程 + flowRecordVerifyService.verifyChildrenRecordsIsEmpty(); + + // 获取流程记录对象 + FlowRecord flowRecord = flowRecordVerifyService.getFlowRecord(); + FlowNode flowNode = flowRecordVerifyService.getFlowNode(); + FlowWork flowWork = flowRecordVerifyService.getFlowWork(); + + // 与当前流程同级的流程记录 + List historyRecords; + if (flowRecord.isStartRecord()) { + historyRecords = new ArrayList<>(); + } else { + historyRecords = flowRecordRepository.findFlowRecordByPreId(flowRecord.getPreId()); + } + + // 获取流程的发起者 + IFlowOperator createOperator = flowRecord.getCreateOperator(); + FlowButton flowButton = flowNode.getButton(buttonId); + if (flowButton == null) { + throw new IllegalArgumentException("flow button not found"); + } + if (!flowButton.hasGroovy()) { + throw new IllegalArgumentException("flow button not groovy"); + } + return flowButton.run(flowRecord, flowNode, flowWork, createOperator, currentOperator, bindData, opinion, historyRecords); + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowDetailService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowDetailService.java new file mode 100644 index 00000000..b2ea7409 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowDetailService.java @@ -0,0 +1,114 @@ +package com.codingapi.springboot.flow.service.impl; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.content.FlowSession; +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.pojo.FlowDetail; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.*; +import com.codingapi.springboot.flow.service.FlowRecordVerifyService; +import com.codingapi.springboot.flow.user.IFlowOperator; +import lombok.AllArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Transactional +@AllArgsConstructor +public class FlowDetailService { + + private final FlowWorkRepository flowWorkRepository; + private final FlowRecordRepository flowRecordRepository; + private final FlowBindDataRepository flowBindDataRepository; + private final FlowOperatorRepository flowOperatorRepository; + private final FlowProcessRepository flowProcessRepository; + + + /** + * 流程详情 + * 如果传递了currentOperator为流程的审批者时,在查看详情的时候可以将流程记录标记为已读 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + */ + public FlowDetail detail(long recordId, IFlowOperator currentOperator) { + + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, + flowProcessRepository, + recordId, currentOperator); + + flowRecordVerifyService.loadFlowRecord(); + flowRecordVerifyService.setFlowRecordRead(); + flowRecordVerifyService.loadFlowWork(); + + FlowRecord flowRecord = flowRecordVerifyService.getFlowRecord(); + FlowWork flowWork = flowRecordVerifyService.getFlowWork(); + + + BindDataSnapshot snapshot = flowBindDataRepository.getBindDataSnapshotById(flowRecord.getSnapshotId()); + List flowRecords = + flowRecordRepository.findFlowRecordByProcessId(flowRecord.getProcessId()). + stream(). + sorted((o1, o2) -> (int) (o2.getId() - o1.getId())) + .collect(Collectors.toList()); + + List operators = new ArrayList<>(); + // 获取所有的操作者 + for (FlowRecord record : flowRecords) { + operators.add(record.getCreateOperator()); + operators.add(record.getCurrentOperator()); + if (record.getInterferedOperator() != null) { + operators.add(record.getInterferedOperator()); + } + } + + return new FlowDetail(flowRecord, snapshot, flowWork, flowRecords, operators, currentOperator != null && flowRecord.isTodo() && flowRecord.isOperator(currentOperator)); + } + + + /** + * 发起流程详情 + * 如果传递了currentOperator为流程的审批者时,在查看详情的时候可以将流程记录标记为已读 + * + * @param workCode 流程记录id + * @param currentOperator 当前操作者 + */ + public FlowDetail detail(String workCode, IFlowOperator currentOperator) { + + if (currentOperator == null) { + throw new IllegalArgumentException("current operator is null"); + } + + FlowWork flowWork = flowWorkRepository.getFlowWorkByCode(workCode); + if (flowWork == null) { + throw new IllegalArgumentException("flow work not found"); + } + flowWork.enableValidate(); + + // 获取开始节点 + FlowNode flowNode = flowWork.getStartNode(); + + FlowSession flowSession = new FlowSession( + null, + flowWork, + flowNode, + currentOperator, + currentOperator, + null, + null, + new ArrayList<>()); + + List operators = flowNode.loadFlowNodeOperator(flowSession, flowOperatorRepository); + + List operatorIds = operators.stream().map(IFlowOperator::getUserId).collect(Collectors.toList()); + if (!operatorIds.contains(currentOperator.getUserId())) { + throw new IllegalArgumentException("current operator is not flow operator"); + } + + return new FlowDetail(flowWork, flowNode, operators,true); + } + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowPostponedService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowPostponedService.java new file mode 100644 index 00000000..6b11b0e5 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowPostponedService.java @@ -0,0 +1,44 @@ +package com.codingapi.springboot.flow.service.impl; + +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.FlowProcessRepository; +import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.service.FlowRecordVerifyService; +import com.codingapi.springboot.flow.user.IFlowOperator; +import lombok.AllArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@AllArgsConstructor +public class FlowPostponedService { + + private final FlowRecordRepository flowRecordRepository; + private final FlowProcessRepository flowProcessRepository; + + /** + * 延期待办 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + * @param time 延期时间 + */ + public void postponed(long recordId, IFlowOperator currentOperator, long time) { + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, + flowProcessRepository, + recordId, currentOperator); + + flowRecordVerifyService.loadFlowRecord(); + flowRecordVerifyService.verifyFlowRecordSubmitState(); + flowRecordVerifyService.verifyFlowRecordCurrentOperator(); + flowRecordVerifyService.loadFlowWork(); + flowRecordVerifyService.verifyFlowRecordNotFinish(); + flowRecordVerifyService.verifyFlowRecordNotDone(); + + FlowRecord flowRecord = flowRecordVerifyService.getFlowRecord(); + FlowWork flowWork = flowRecordVerifyService.getFlowWork(); + + flowRecord.postponedTime(flowWork.getPostponedMax(), time); + flowRecordRepository.update(flowRecord); + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRecallService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRecallService.java new file mode 100644 index 00000000..e0d98d0a --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRecallService.java @@ -0,0 +1,73 @@ +package com.codingapi.springboot.flow.service.impl; + +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.event.FlowApprovalEvent; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.FlowProcessRepository; +import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.service.FlowRecordVerifyService; +import com.codingapi.springboot.flow.user.IFlowOperator; +import com.codingapi.springboot.framework.event.EventPusher; +import lombok.AllArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collections; +import java.util.List; + +@Transactional +@AllArgsConstructor +public class FlowRecallService { + + private final FlowRecordRepository flowRecordRepository; + private final FlowProcessRepository flowProcessRepository; + + /** + * 撤回流程 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + */ + public void recall(long recordId, IFlowOperator currentOperator) { + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, + flowProcessRepository, + recordId, currentOperator); + + flowRecordVerifyService.loadFlowRecord(); + flowRecordVerifyService.verifyFlowRecordCurrentOperator(); + flowRecordVerifyService.loadFlowWork(); + flowRecordVerifyService.loadFlowNode(); + flowRecordVerifyService.verifyFlowRecordNotFinish(); + flowRecordVerifyService.verifyFlowRecordNotTodo(); + + FlowRecord flowRecord = flowRecordVerifyService.getFlowRecord(); + FlowWork flowWork = flowRecordVerifyService.getFlowWork(); + + // 下一流程的流程记录 + List childrenRecords = flowRecordRepository.findFlowRecordByPreId(recordId); + // 下一流程均为办理且未读 + + // 如果是在开始节点撤销,则直接删除 + if (flowRecord.isStartRecord() && flowRecord.isTodo()) { + if (!childrenRecords.isEmpty()) { + throw new IllegalArgumentException("flow record not recall"); + } + flowRecordRepository.delete(Collections.singletonList(flowRecord)); + } else { + // 如果是在中间节点撤销,则需要判断是否所有的子流程都是未读状态 + if (childrenRecords.isEmpty()) { + throw new IllegalArgumentException("flow record not submit"); + } + + boolean allUnDone = childrenRecords.stream().allMatch(item -> item.isUnRead() && item.isTodo()); + if (!allUnDone) { + throw new IllegalArgumentException("flow record not recall"); + } + flowRecord.recall(); + flowRecordRepository.update(flowRecord); + + flowRecordRepository.delete(childrenRecords); + } + + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_RECALL, flowRecord, currentOperator, flowWork, null), true); + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSaveService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSaveService.java new file mode 100644 index 00000000..174c18a3 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSaveService.java @@ -0,0 +1,51 @@ +package com.codingapi.springboot.flow.service.impl; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.bind.IBindData; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.FlowBindDataRepository; +import com.codingapi.springboot.flow.repository.FlowProcessRepository; +import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.service.FlowRecordVerifyService; +import com.codingapi.springboot.flow.user.IFlowOperator; +import lombok.AllArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@AllArgsConstructor +public class FlowSaveService { + + private final FlowRecordRepository flowRecordRepository; + private final FlowBindDataRepository flowBindDataRepository; + private final FlowProcessRepository flowProcessRepository; + + /** + * 保存流程 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + * @param bindData 绑定数据 + * @param advice 审批意见 + */ + public void save(long recordId, IFlowOperator currentOperator, IBindData bindData, String advice) { + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, + flowProcessRepository, + recordId, currentOperator); + flowRecordVerifyService.loadFlowRecord(); + flowRecordVerifyService.verifyFlowRecordSubmitState(); + flowRecordVerifyService.verifyFlowRecordCurrentOperator(); + flowRecordVerifyService.loadFlowWork(); + flowRecordVerifyService.loadFlowNode(); + flowRecordVerifyService.verifyFlowNodeEditableState(false); + + Opinion opinion = Opinion.save(advice); + FlowRecord flowRecord = flowRecordVerifyService.getFlowRecord(); + BindDataSnapshot snapshot = new BindDataSnapshot(flowRecord.getSnapshotId(), bindData); + flowBindDataRepository.update(snapshot); + + flowRecord.setOpinion(opinion); + flowRecordRepository.update(flowRecord); + } + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStartService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStartService.java new file mode 100644 index 00000000..80047a84 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStartService.java @@ -0,0 +1,142 @@ +package com.codingapi.springboot.flow.service.impl; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.bind.IBindData; +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.FlowSourceDirection; +import com.codingapi.springboot.flow.event.FlowApprovalEvent; +import com.codingapi.springboot.flow.pojo.FlowResult; +import com.codingapi.springboot.flow.record.FlowBackup; +import com.codingapi.springboot.flow.record.FlowProcess; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.*; +import com.codingapi.springboot.flow.service.FlowNodeService; +import com.codingapi.springboot.flow.user.IFlowOperator; +import com.codingapi.springboot.framework.event.EventPusher; +import lombok.AllArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Transactional +@AllArgsConstructor +public class FlowStartService { + + private final FlowWorkRepository flowWorkRepository; + private final FlowRecordRepository flowRecordRepository; + private final FlowBindDataRepository flowBindDataRepository; + private final FlowOperatorRepository flowOperatorRepository; + private final FlowProcessRepository flowProcessRepository; + private final FlowBackupRepository flowBackupRepository; + + + /** + * 发起流程 (不自动提交到下一节点) + * + * @param workCode 流程编码 + * @param operator 操作者 + * @param bindData 绑定数据 + * @param advice 审批意见 + */ + public FlowResult startFlow(String workCode, IFlowOperator operator, IBindData bindData, String advice) { + // 检测流程是否存在 + FlowWork flowWork = flowWorkRepository.getFlowWorkByCode(workCode); + if (flowWork == null) { + throw new IllegalArgumentException("flow work not found"); + } + flowWork.verify(); + flowWork.enableValidate(); + + // 流程数据备份 + FlowBackup flowBackup = flowBackupRepository.getFlowBackupByWorkIdAndVersion(flowWork.getId(), flowWork.getUpdateTime()); + if (flowBackup == null) { + flowBackup = flowBackupRepository.backup(flowWork); + } + + // 保存流程 + FlowProcess flowProcess = new FlowProcess(flowBackup.getId(), operator); + flowProcessRepository.save(flowProcess); + + // 保存绑定数据 + BindDataSnapshot snapshot = new BindDataSnapshot(bindData); + flowBindDataRepository.save(snapshot); + + // 创建流程id + String processId = flowProcess.getProcessId(); + + // 构建审批意见 + Opinion opinion = Opinion.pass(advice); + + // 获取开始节点 + FlowNode start = flowWork.getStartNode(); + if (start == null) { + throw new IllegalArgumentException("start node not found"); + } + // 设置开始流程的上一个流程id + long preId = 0; + + List historyRecords = new ArrayList<>(); + + FlowNodeService flowNodeService = new FlowNodeService(flowOperatorRepository, + flowRecordRepository, + snapshot, + opinion, + operator, + operator, + historyRecords, + flowWork, + null, + processId, + preId); + + flowNodeService.setNextNode(start); + + // 创建待办记录 + List records = flowNodeService.createRecord(); + if (records.isEmpty()) { + throw new IllegalArgumentException("flow record not found"); + } else { + for (FlowRecord record : records) { + record.updateOpinion(opinion); + } + } + + // 检测流程是否结束 + if (flowNodeService.nextNodeIsOver()) { + for (FlowRecord record : records) { + record.submitRecord(operator, snapshot, opinion, FlowSourceDirection.PASS); + record.finish(); + } + + flowRecordRepository.save(records); + + // 推送事件 + for (FlowRecord record : records) { + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_CREATE, record, operator, flowWork, snapshot.toBindData()), true); + + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_FINISH, + record, + operator, + flowWork, + snapshot.toBindData()), + true); + } + return new FlowResult(flowWork, records); + } + + // 保存流程记录 + flowRecordRepository.save(records); + + // 推送事件消息 + for (FlowRecord record : records) { + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_CREATE, record, operator, flowWork, snapshot.toBindData()), true); + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, record, operator, flowWork, snapshot.toBindData()), true); + } + // 当前的审批记录 + return new FlowResult(flowWork, records); + } + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java new file mode 100644 index 00000000..77d379c6 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java @@ -0,0 +1,196 @@ +package com.codingapi.springboot.flow.service.impl; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.bind.IBindData; +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.FlowSourceDirection; +import com.codingapi.springboot.flow.event.FlowApprovalEvent; +import com.codingapi.springboot.flow.pojo.FlowResult; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.FlowBindDataRepository; +import com.codingapi.springboot.flow.repository.FlowOperatorRepository; +import com.codingapi.springboot.flow.repository.FlowProcessRepository; +import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.service.FlowDirectionService; +import com.codingapi.springboot.flow.service.FlowNodeService; +import com.codingapi.springboot.flow.service.FlowRecordVerifyService; +import com.codingapi.springboot.flow.user.IFlowOperator; +import com.codingapi.springboot.framework.event.EventPusher; +import lombok.AllArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Transactional +@AllArgsConstructor +public class FlowSubmitService { + + private final FlowRecordRepository flowRecordRepository; + private final FlowBindDataRepository flowBindDataRepository; + private final FlowOperatorRepository flowOperatorRepository; + private final FlowProcessRepository flowProcessRepository; + + /** + * 提交流程 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + * @param bindData 绑定数据 + * @param opinion 审批意见 + */ + public FlowResult submitFlow(long recordId, IFlowOperator currentOperator, IBindData bindData, Opinion opinion) { + + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, flowProcessRepository, recordId, currentOperator); + + // 加载流程 + flowRecordVerifyService.loadFlowRecord(); + // 验证流程的提交状态 + flowRecordVerifyService.verifyFlowRecordSubmitState(); + // 验证当前操作者 + flowRecordVerifyService.verifyFlowRecordCurrentOperator(); + // 加载流程设计 + flowRecordVerifyService.loadFlowWork(); + // 加载流程节点 + flowRecordVerifyService.loadFlowNode(); + // 验证没有子流程 + flowRecordVerifyService.verifyChildrenRecordsIsEmpty(); + + // 获取流程记录对象 + FlowRecord flowRecord = flowRecordVerifyService.getFlowRecord(); + FlowNode flowNode = flowRecordVerifyService.getFlowNode(); + FlowWork flowWork = flowRecordVerifyService.getFlowWork(); + + + // 保存流程表单快照数据 + BindDataSnapshot snapshot = null; + if (flowNode.isEditable()) { + snapshot = new BindDataSnapshot(bindData); + flowBindDataRepository.save(snapshot); + } else { + snapshot = flowBindDataRepository.getBindDataSnapshotById(flowRecord.getSnapshotId()); + } + + // 审批方向判断服务 + FlowDirectionService flowDirectionService = new FlowDirectionService(flowRecordVerifyService.getFlowNode(), flowRecordVerifyService.getFlowWork(), opinion); + + // 加载流程审批方向 + flowDirectionService.loadFlowSourceDirection(); + // 验证审批方向 + flowDirectionService.verifyFlowSourceDirection(); + + // 根据当前方向提交流程 + FlowSourceDirection flowSourceDirection = flowDirectionService.getFlowSourceDirection(); + flowRecord.submitRecord(currentOperator, snapshot, opinion, flowSourceDirection); + flowRecordRepository.update(flowRecord); + + // 与当前流程同级的流程记录 + List historyRecords; + if (flowRecord.isStartRecord()) { + historyRecords = new ArrayList<>(); + } else { + historyRecords = flowRecordRepository.findFlowRecordByPreId(flowRecord.getPreId()); + } + flowDirectionService.bindHistoryRecords(historyRecords); + + // 判断流程是否结束(会签时需要所有人都通过) + if (flowNode.isSign()) { + boolean next = flowDirectionService.hasCurrentFlowNodeIsDone(); + if (next) { + List todoRecords = historyRecords.stream().filter(FlowRecord::isTodo).collect(Collectors.toList()); + return new FlowResult(flowWork, todoRecords); + } + } + + // 非会签下,当有人提交以后,将所有未提交的流程都自动提交,然后再执行下一节点 + if (flowNode.isUnSign()) { + for (FlowRecord record : historyRecords) { + if (record.isTodo() && record.getId() != flowRecord.getId()) { + record.autoPass(currentOperator, snapshot); + flowRecordRepository.update(flowRecord); + } + } + } + + // 根据所有提交意见,重新加载审批方向 + flowSourceDirection = flowDirectionService.reloadFlowSourceDirection(); + + // 获取流程的发起者 + IFlowOperator createOperator = flowRecord.getCreateOperator(); + + // 构建流程创建器 + FlowNodeService flowNodeService = new FlowNodeService( + flowOperatorRepository, + flowRecordRepository, + snapshot, + opinion, + createOperator, + currentOperator, + historyRecords, + flowWork, + flowRecord, + flowRecord.getProcessId(), + flowRecord.getId() + ); + + // 审批通过并进入下一节点 + if (flowDirectionService.isPassRecord()) { + flowNodeService.loadNextPassNode(flowNode); + // 审批拒绝返回上一节点 + } else if (flowDirectionService.isDefaultBackRecord()) { + flowNodeService.loadDefaultBackNode(flowRecord.getPreId()); + } else { + // 审批拒绝,并且自定了返回节点 + flowNodeService.loadCustomBackNode(flowNode, flowRecord.getPreId()); + } + + List records = flowNodeService.createRecord(); + + // 判断流程是否完成 + if (flowNodeService.nextNodeIsOver()) { + flowRecord.submitRecord(currentOperator, snapshot, opinion, flowSourceDirection); + flowRecord.finish(); + flowRecordRepository.update(flowRecord); + flowRecordRepository.finishFlowRecordByProcessId(flowRecord.getProcessId()); + + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_FINISH, + flowRecord, + currentOperator, + flowWork, + snapshot.toBindData()), + true); + if(!records.isEmpty()) { + return new FlowResult(flowWork, records.get(0)); + } + return new FlowResult(flowWork, flowRecord); + } + + // 保存流程记录 + flowRecordRepository.save(records); + + // 推送审批事件消息 + int eventState = flowSourceDirection == FlowSourceDirection.PASS ? FlowApprovalEvent.STATE_PASS : FlowApprovalEvent.STATE_REJECT; + EventPusher.push(new FlowApprovalEvent(eventState, + flowRecord, + currentOperator, + flowWork, + snapshot.toBindData()), + true); + + // 推送待办事件消息 + for (FlowRecord record : records) { + IFlowOperator pushOperator = record.getCurrentOperator(); + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, + record, + pushOperator, + flowWork, + snapshot.toBindData()), + true); + } + + return new FlowResult(flowWork, records); + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTransferService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTransferService.java new file mode 100644 index 00000000..429d79f7 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTransferService.java @@ -0,0 +1,100 @@ +package com.codingapi.springboot.flow.service.impl; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.bind.IBindData; +import com.codingapi.springboot.flow.content.FlowSession; +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.event.FlowApprovalEvent; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.FlowBindDataRepository; +import com.codingapi.springboot.flow.repository.FlowProcessRepository; +import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.service.FlowRecordVerifyService; +import com.codingapi.springboot.flow.user.IFlowOperator; +import com.codingapi.springboot.framework.event.EventPusher; +import lombok.AllArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Transactional +@AllArgsConstructor +public class FlowTransferService { + + private final FlowRecordRepository flowRecordRepository; + private final FlowBindDataRepository flowBindDataRepository; + private final FlowProcessRepository flowProcessRepository; + + + /** + * 转办流程 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + * @param targetOperator 转办操作者 + * @param bindData 绑定数据 + * @param advice 转办意见 + */ + public void transfer(long recordId, IFlowOperator currentOperator, IFlowOperator targetOperator, IBindData bindData, String advice) { + + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, + flowProcessRepository, + recordId, currentOperator); + + flowRecordVerifyService.loadFlowRecord(); + flowRecordVerifyService.verifyFlowRecordSubmitState(); + flowRecordVerifyService.verifyFlowRecordCurrentOperator(); + flowRecordVerifyService.verifyTargetOperatorIsNotCurrentOperator(targetOperator); + + flowRecordVerifyService.loadFlowWork(); + flowRecordVerifyService.loadFlowNode(); + + flowRecordVerifyService.verifyFlowRecordIsTodo(); + + FlowRecord flowRecord = flowRecordVerifyService.getFlowRecord(); + FlowWork flowWork = flowRecordVerifyService.getFlowWork(); + FlowNode flowNode = flowRecordVerifyService.getFlowNode(); + + + // 保存绑定数据 + BindDataSnapshot snapshot = new BindDataSnapshot(bindData); + flowBindDataRepository.save(snapshot); + + // 构建审批意见 + Opinion opinion = Opinion.transfer(advice); + + // 设置自己的流程状态为转办已完成 + flowRecord.transfer(currentOperator, snapshot, opinion); + flowRecordRepository.update(flowRecord); + + // 获取创建者 + IFlowOperator createOperator = flowRecord.getCreateOperator(); + + // 与当前流程同级的流程记录 + List historyRecords; + if (flowRecord.isStartRecord()) { + historyRecords = new ArrayList<>(); + } else { + historyRecords = flowRecordRepository.findFlowRecordByPreId(flowRecord.getPreId()); + } + + // 创建新的待办标题 + FlowSession content = new FlowSession(flowRecord, flowWork, flowNode, createOperator, targetOperator, snapshot.toBindData(), opinion, historyRecords); + String generateTitle = flowNode.generateTitle(content); + + // 创建转办记录 + FlowRecord transferRecord = flowRecord.copy(); + transferRecord.transferToTodo(generateTitle, targetOperator); + flowRecordRepository.save(Collections.singletonList(transferRecord)); + + // 推送转办消息 + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TRANSFER, flowRecord, currentOperator, flowWork, snapshot.toBindData()), true); + + // 推送待办消息 + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, transferRecord, targetOperator, flowWork, snapshot.toBindData()), true); + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTrySubmitService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTrySubmitService.java new file mode 100644 index 00000000..c48fab7f --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTrySubmitService.java @@ -0,0 +1,250 @@ +package com.codingapi.springboot.flow.service.impl; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.bind.IBindData; +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.FlowSourceDirection; +import com.codingapi.springboot.flow.pojo.FlowSubmitResult; +import com.codingapi.springboot.flow.record.FlowBackup; +import com.codingapi.springboot.flow.record.FlowProcess; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.*; +import com.codingapi.springboot.flow.service.FlowDirectionService; +import com.codingapi.springboot.flow.service.FlowNodeService; +import com.codingapi.springboot.flow.service.FlowRecordVerifyService; +import com.codingapi.springboot.flow.user.IFlowOperator; +import lombok.AllArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Transactional +@AllArgsConstructor +public class FlowTrySubmitService { + + private final FlowRecordRepository flowRecordRepository; + private final FlowBindDataRepository flowBindDataRepository; + private final FlowOperatorRepository flowOperatorRepository; + private final FlowProcessRepository flowProcessRepository; + private final FlowWorkRepository flowWorkRepository; + private final FlowBackupRepository flowBackupRepository; + + + /** + * 尝试提交流程 (流程过程中) + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + * @param bindData 绑定数据 + * @param opinion 审批意见 + */ + public FlowSubmitResult trySubmitFlow(long recordId, IFlowOperator currentOperator, IBindData bindData, Opinion opinion) { + + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, flowProcessRepository, recordId, currentOperator); + + // 加载流程 + flowRecordVerifyService.loadFlowRecord(); + // 验证流程的提交状态 + flowRecordVerifyService.verifyFlowRecordSubmitState(); + // 验证当前操作者 + flowRecordVerifyService.verifyFlowRecordCurrentOperator(); + // 加载流程设计 + flowRecordVerifyService.loadFlowWork(); + // 加载流程节点 + flowRecordVerifyService.loadFlowNode(); + // 验证没有子流程 + flowRecordVerifyService.verifyChildrenRecordsIsEmpty(); + + // 获取流程记录对象 copy 流程数据防止影响原有数据 + FlowRecord flowRecord = flowRecordVerifyService.getFlowRecord().copy(); + FlowNode flowNode = flowRecordVerifyService.getFlowNode(); + FlowWork flowWork = flowRecordVerifyService.getFlowWork(); + + return trySubmitFlow(flowWork, flowNode, flowRecord, currentOperator, bindData, opinion); + } + + + /** + * 预提交流程数据查询 + * + * @param flowWork 流程设计 + * @param flowNode 流程节点 + * @param flowRecord 流程记录 + * @param currentOperator 当前操作者 + * @param bindData 绑定数据 + * @param opinion 审批意见 + * @return FlowSubmitResult + */ + private FlowSubmitResult trySubmitFlow(FlowWork flowWork, FlowNode flowNode, FlowRecord flowRecord, IFlowOperator currentOperator, IBindData bindData, Opinion opinion) { + + // 保存流程表单快照数据 + BindDataSnapshot snapshot = null; + if (flowNode.isEditable()) { + snapshot = new BindDataSnapshot(bindData); + } else { + snapshot = flowBindDataRepository.getBindDataSnapshotById(flowRecord.getSnapshotId()); + } + + // 审批方向判断服务 + FlowDirectionService flowDirectionService = new FlowDirectionService(flowNode, flowWork, opinion); + + // 加载流程审批方向 + flowDirectionService.loadFlowSourceDirection(); + // 验证审批方向 + flowDirectionService.verifyFlowSourceDirection(); + + // 根据当前方向提交流程 + FlowSourceDirection flowSourceDirection = flowDirectionService.getFlowSourceDirection(); + flowRecord.submitRecord(currentOperator, snapshot, opinion, flowSourceDirection); + + // 与当前流程同级的流程记录 + List historyRecords; + if (flowRecord.isStartRecord()) { + historyRecords = new ArrayList<>(); + } else { + // copy 流程数据防止影响原有数据 + historyRecords = flowRecordRepository.findFlowRecordByPreId(flowRecord.getPreId()).stream().map(FlowRecord::copy).collect(Collectors.toList()); + } + flowDirectionService.bindHistoryRecords(historyRecords); + + // 判断流程是否结束(会签时需要所有人都通过) + if (flowNode.isSign()) { + boolean next = flowDirectionService.hasCurrentFlowNodeIsDone(); + if (next) { + List todoRecords = historyRecords.stream().filter(FlowRecord::isTodo).collect(Collectors.toList()); + return new FlowSubmitResult(flowWork, flowNode, todoRecords.stream().map(FlowRecord::getCurrentOperator).collect(Collectors.toList())); + } + } + + // 非会签下,当有人提交以后,将所有未提交的流程都自动提交,然后再执行下一节点 + if (flowNode.isUnSign()) { + for (FlowRecord record : historyRecords) { + if (record.isTodo() && record.getId() != flowRecord.getId()) { + record.autoPass(currentOperator, snapshot); + } + } + } + + // 根据所有提交意见,重新加载审批方向 + flowDirectionService.reloadFlowSourceDirection(); + + // 获取流程的发起者 + IFlowOperator createOperator = flowRecord.getCreateOperator(); + + // 构建流程创建器 + FlowNodeService flowNodeService = new FlowNodeService( + flowOperatorRepository, + flowRecordRepository, + snapshot, + opinion, + createOperator, + currentOperator, + historyRecords, + flowWork, + flowRecord, + flowRecord.getProcessId(), + flowRecord.getId() + ); + + // 审批通过并进入下一节点 + if (flowDirectionService.isPassRecord()) { + flowNodeService.loadNextPassNode(flowNode); + // 审批拒绝返回上一节点 + } else if (flowDirectionService.isDefaultBackRecord()) { + flowNodeService.loadDefaultBackNode(flowRecord.getPreId()); + } else { + // 审批拒绝,并且自定了返回节点 + flowNodeService.loadCustomBackNode(flowNode, flowRecord.getPreId()); + } + + FlowNode nextNode = flowNodeService.getNextNode(); + + List operators = flowNodeService.loadNextNodeOperators(); + return new FlowSubmitResult(flowWork, nextNode, operators); + } + + + /** + * 尝试提交流程 (发起流程) + * + * @param workCode 流程编码 + * @param currentOperator 当前操作者 + * @param bindData 绑定数据 + * @param opinion 审批意见 + */ + public FlowSubmitResult trySubmitFlow(String workCode, IFlowOperator currentOperator, IBindData bindData, Opinion opinion) { + // 检测流程是否存在 + FlowWork flowWork = flowWorkRepository.getFlowWorkByCode(workCode); + if (flowWork == null) { + throw new IllegalArgumentException("flow work not found"); + } + flowWork.verify(); + flowWork.enableValidate(); + + // 流程数据备份 + FlowBackup flowBackup = flowBackupRepository.getFlowBackupByWorkIdAndVersion(flowWork.getId(), flowWork.getUpdateTime()); + if (flowBackup == null) { + flowBackup = flowBackupRepository.backup(flowWork); + } + + // 保存流程 + FlowProcess flowProcess = new FlowProcess(flowBackup.getId(), currentOperator); + + // 保存绑定数据 + BindDataSnapshot snapshot = new BindDataSnapshot(bindData); + + // 创建流程id + String processId = flowProcess.getProcessId(); + + // 获取开始节点 + FlowNode start = flowWork.getStartNode(); + if (start == null) { + throw new IllegalArgumentException("start node not found"); + } + // 设置开始流程的上一个流程id + long preId = 0; + + List historyRecords = new ArrayList<>(); + + FlowNodeService flowNodeService = new FlowNodeService(flowOperatorRepository, + flowRecordRepository, + snapshot, + opinion, + currentOperator, + currentOperator, + historyRecords, + flowWork, + null, + processId, + preId); + + flowNodeService.setNextNode(start); + + FlowRecord startRecord = null; + + // 创建待办记录 + List records = flowNodeService.createRecord(); + if (records.isEmpty()) { + throw new IllegalArgumentException("flow record not found"); + } else { + for (FlowRecord record : records) { + record.updateOpinion(opinion); + startRecord = record; + } + } + + // 检测流程是否结束 + if (flowNodeService.nextNodeIsOver()) { + for (FlowRecord record : records) { + record.submitRecord(currentOperator, snapshot, opinion, FlowSourceDirection.PASS); + record.finish(); + startRecord = record; + } + } + return this.trySubmitFlow(flowWork, start, startRecord, currentOperator, bindData, opinion); + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowUrgeService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowUrgeService.java new file mode 100644 index 00000000..2ebfbfb8 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowUrgeService.java @@ -0,0 +1,50 @@ +package com.codingapi.springboot.flow.service.impl; + +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.event.FlowApprovalEvent; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.FlowProcessRepository; +import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.service.FlowRecordVerifyService; +import com.codingapi.springboot.flow.user.IFlowOperator; +import com.codingapi.springboot.framework.event.EventPusher; +import lombok.AllArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Transactional +@AllArgsConstructor +public class FlowUrgeService { + + + private final FlowRecordRepository flowRecordRepository; + private final FlowProcessRepository flowProcessRepository; + + /** + * 催办流程 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + */ + public void urge(long recordId, IFlowOperator currentOperator) { + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, + flowProcessRepository, + recordId, currentOperator); + flowRecordVerifyService.loadFlowRecord(); + flowRecordVerifyService.loadFlowWork(); + flowRecordVerifyService.verifyFlowRecordIsDone(); + + FlowRecord flowRecord = flowRecordVerifyService.getFlowRecord(); + FlowWork flowWork = flowRecordVerifyService.getFlowWork(); + + List todoRecords = flowRecordRepository.findTodoFlowRecordByProcessId(flowRecord.getProcessId()); + + // 推送催办消息 + for (FlowRecord record : todoRecords) { + IFlowOperator pushOperator = record.getCurrentOperator(); + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_URGE, record, pushOperator, flowWork, null), true); + } + + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java index 36cfde5c..02d88d4b 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java @@ -43,8 +43,6 @@ public List findFlowRecordByPreId(long preId) { return cache.stream().filter(record -> record.getPreId() == preId).collect(Collectors.toList()); } - - @Override public List findFlowRecordByProcessId(String processId) { return cache.stream().filter(record -> record.getProcessId().equals(processId)) @@ -61,13 +59,24 @@ public Page findAll(PageRequest pageRequest) { return new PageImpl<>(cache); } - @Override - public Page findDoneByOperatorId(long operatorId,PageRequest pageRequest) { + public Page findDoneByOperatorId(long operatorId, PageRequest pageRequest) { List flowRecords = cache.stream().filter(record -> record.isDone() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList()); return new PageImpl<>(flowRecords); } + @Override + public Page findUnReadByOperatorId(long operatorId, PageRequest pageRequest) { + List flowRecords = cache.stream().filter(record -> record.isUnRead() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList()); + return new PageImpl<>(flowRecords); + } + + @Override + public Page findUnReadByOperatorId(long operatorId, String workCode, PageRequest pageRequest) { + List flowRecords = cache.stream().filter(record -> record.isUnRead() && record.getWorkCode().equals(workCode) && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList()); + return new PageImpl<>(flowRecords); + } + @Override public Page findDoneByOperatorId(long operatorId, String workCode, PageRequest pageRequest) { List flowRecords = cache.stream().filter(record -> record.isDone() @@ -78,7 +87,7 @@ public Page findDoneByOperatorId(long operatorId, String workCode, P } @Override - public Page findInitiatedByOperatorId(long operatorId,PageRequest pageRequest) { + public Page findInitiatedByOperatorId(long operatorId, PageRequest pageRequest) { List flowRecords = cache.stream().filter(record -> record.isInitiated() && record.getCreateOperator().getUserId() == operatorId).collect(Collectors.toList()); return new PageImpl<>(flowRecords); } @@ -96,7 +105,7 @@ record -> record.isInitiated() @Override - public Page findTodoByOperatorId(long operatorId,PageRequest pageRequest) { + public Page findTodoByOperatorId(long operatorId, PageRequest pageRequest) { List flowRecords = cache.stream().filter(record -> record.isTodo() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList()); return new PageImpl<>(flowRecords); } @@ -108,13 +117,12 @@ public Page findTodoByOperatorId(long operatorId, String workCode, P } @Override - public Page findTimeoutTodoByOperatorId(long operatorId,PageRequest pageRequest) { + public Page findTimeoutTodoByOperatorId(long operatorId, PageRequest pageRequest) { List flowRecords = cache.stream().filter(record -> record.isTimeout() && record.isTodo() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList()); return new PageImpl<>(flowRecords); } - @Override public Page findTimeoutTodoByOperatorId(long operatorId, String workCode, PageRequest pageRequest) { List flowRecords = cache.stream().filter( @@ -127,7 +135,7 @@ record -> record.isTimeout() @Override - public Page findPostponedTodoByOperatorId(long operatorId,PageRequest pageRequest) { + public Page findPostponedTodoByOperatorId(long operatorId, PageRequest pageRequest) { List flowRecords = cache.stream().filter(record -> record.isPostponed() && record.isTodo() && record.getCurrentOperator().getUserId() == operatorId).collect(Collectors.toList()); return new PageImpl<>(flowRecords); } diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/BuildTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/BuildTest.java index 47c426a6..e9c739a9 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/BuildTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/BuildTest.java @@ -27,15 +27,18 @@ void build() { .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("抄送节点", "circulate", "default", ApprovalType.CIRCULATE, OperatorMatcher.anyOperatorMatcher()) .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") - .relation("结束节点", "manager", "over") + .relation("抄送节点", "manager", "circulate") + .relation("结束节点", "circulate", "over") .build(); assertEquals("请假流程", flowWork.getTitle()); - assertEquals(4, flowWork.getNodes().size()); - assertEquals(3, flowWork.getRelations().size()); + assertEquals(5, flowWork.getNodes().size()); + assertEquals(4, flowWork.getRelations().size()); byte[] bytes = flowWork.toSerializable().toSerializable(); diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/CirculateTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/CirculateTest.java new file mode 100644 index 00000000..7e198d49 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/CirculateTest.java @@ -0,0 +1,138 @@ +package com.codingapi.springboot.flow.test; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.build.FlowWorkBuilder; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.flow.Leave; +import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.pojo.FlowDetail; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.*; +import com.codingapi.springboot.flow.service.FlowService; +import com.codingapi.springboot.flow.user.User; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class CirculateTest { + + + private final UserRepository userRepository = new UserRepository(); + private final FlowWorkRepository flowWorkRepository = new FlowWorkRepositoryImpl(); + private final FlowRecordRepositoryImpl flowRecordRepository = new FlowRecordRepositoryImpl(); + private final FlowBindDataRepositoryImpl flowBindDataRepository = new FlowBindDataRepositoryImpl(); + private final LeaveRepository leaveRepository = new LeaveRepository(); + private final FlowBackupRepository flowBackupRepository = new FlowBackupRepositoryImpl(); + private final FlowProcessRepository flowProcessRepository = new FlowProcessRepositoryImpl(flowBackupRepository,userRepository); + private final FlowService flowService = new FlowService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, userRepository,flowProcessRepository,flowBackupRepository); + + + @Test + void circulate(){ + PageRequest pageRequest = PageRequest.of(0, 1000); + + User lorne = new User("lorne"); + userRepository.save(lorne); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("抄送节点1", "circulate1", "default", ApprovalType.CIRCULATE, OperatorMatcher.specifyOperatorMatcher(lorne.getUserId())) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("抄送节点2", "circulate2", "default", ApprovalType.CIRCULATE, OperatorMatcher.specifyOperatorMatcher(user.getUserId())) + .node("抄送节点3", "circulate3", "default", ApprovalType.CIRCULATE, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + + .relations() + .relation("部门领导审批", "start", "dept") + .relation("抄送节点1", "dept", "circulate1") + .relation("总经理审批", "circulate1", "manager") + .relation("抄送节点1", "manager", "circulate2") + .relation("抄送节点2", "circulate2", "circulate3") + .relation("结束节点", "circulate3", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findUnReadByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + // 保存流程 + leave.setTitle("我要出去看看~~"); + flowService.save(userTodo.getId(), user, leave,"暂存"); + + // 查看流程详情 + FlowDetail flowDetail = flowService.detail(userTodo.getId(), user); + assertEquals("我要出去看看~~", ((Leave) flowDetail.getBindData()).getTitle()); + assertTrue(flowDetail.getFlowRecord().isRead()); + + + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(6, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(0, userTodos.size()); + + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(6, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(4, snapshots.size()); + + + // 查看lorne的未读 + List lorneTodos = flowRecordRepository.findUnReadByOperatorId(lorne.getUserId(), pageRequest).getContent(); + assertEquals(1, lorneTodos.size()); + + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ErrorTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ErrorTest.java index 219b8472..97010832 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ErrorTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ErrorTest.java @@ -55,7 +55,7 @@ void errorMatcherOperatorTest(){ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, new OperatorMatcher("def run(content){return []}"), new ErrTrigger("def run(content){return content.createOperatorErrTrigger("+dept.getId()+")}"), true) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") @@ -108,21 +108,18 @@ void errorMatcherOperatorTest(){ // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); - - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(0, userTodos.size()); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(5, snapshots.size()); + assertEquals(4, snapshots.size()); } @@ -150,7 +147,7 @@ void errorMatcherNodeTest(){ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, new OperatorMatcher("def run(content){return []}"), new ErrTrigger("def run(content){return content.createNodeErrTrigger('manager')}"), true) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") @@ -199,20 +196,17 @@ void errorMatcherNodeTest(){ // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(3, records.size()); + assertEquals(2, records.size()); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); - - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(0, userTodos.size()); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(3, records.size()); + assertEquals(2, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(4, snapshots.size()); + assertEquals(3, snapshots.size()); } } diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java index 40bcb8b4..113facaa 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java @@ -8,14 +8,17 @@ import com.codingapi.springboot.flow.flow.Leave; import com.codingapi.springboot.flow.matcher.OperatorMatcher; import com.codingapi.springboot.flow.pojo.FlowDetail; +import com.codingapi.springboot.flow.pojo.FlowSubmitResult; import com.codingapi.springboot.flow.record.FlowRecord; import com.codingapi.springboot.flow.repository.*; import com.codingapi.springboot.flow.service.FlowService; +import com.codingapi.springboot.flow.user.IFlowOperator; import com.codingapi.springboot.flow.user.User; import org.junit.jupiter.api.Test; import org.springframework.data.domain.PageRequest; import java.util.List; +import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; @@ -55,7 +58,7 @@ void entrustTest() { .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") @@ -112,21 +115,18 @@ void entrustTest() { // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); - - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(0, userTodos.size()); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(5, snapshots.size()); + assertEquals(4, snapshots.size()); } @@ -183,18 +183,15 @@ void passAndRejectTest() { flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); - - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(0, userTodos.size()); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(3, records.size()); + assertEquals(2, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(4, snapshots.size()); + assertEquals(3, snapshots.size()); } @@ -220,7 +217,7 @@ void passTest() { .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") @@ -273,21 +270,19 @@ void passTest() { // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); + assertEquals(0, userTodos.size()); - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(5, snapshots.size()); + assertEquals(4, snapshots.size()); } @@ -314,7 +309,7 @@ void saveDisableTest() { .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId()), false) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") @@ -359,21 +354,18 @@ void saveDisableTest() { // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); - - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(0, userTodos.size()); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(4, snapshots.size()); + assertEquals(3, snapshots.size()); } @@ -448,21 +440,18 @@ void interfereTest() { // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); - - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(0, userTodos.size()); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(5, snapshots.size()); + assertEquals(4, snapshots.size()); } @@ -491,7 +480,7 @@ void transferTest() { .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") @@ -547,21 +536,18 @@ void transferTest() { // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(5, records.size()); + assertEquals(4, records.size()); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); - - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(0, userTodos.size()); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(5, records.size()); + assertEquals(4, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(6, snapshots.size()); + assertEquals(5, snapshots.size()); } @@ -668,7 +654,7 @@ void rejectTest() { .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") @@ -727,7 +713,7 @@ void rejectTest() { flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(6, records.size()); + assertEquals(5, records.size()); } @@ -737,9 +723,12 @@ void rejectTest() { * 撤销流程测试 */ @Test - void recallTest() { + void recallTest1() { PageRequest pageRequest = PageRequest.of(0, 1000); + User lorne = new User("lorne"); + userRepository.save(lorne); + User user = new User("张飞"); userRepository.save(user); @@ -753,9 +742,9 @@ void recallTest() { .title("请假流程") .nodes() .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) - .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId(),lorne.getUserId())) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") @@ -778,14 +767,21 @@ void recallTest() { // 提交流程 FlowRecord userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + FlowSubmitResult flowSubmitResult = flowService.trySubmitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(flowSubmitResult.getOperators().size(), 2); + assertTrue(flowSubmitResult.getOperators().stream().map(IFlowOperator::getUserId).collect(Collectors.toList()).contains(dept.getUserId())); + assertTrue(flowSubmitResult.getOperators().stream().map(IFlowOperator::getUserId).collect(Collectors.toList()).contains(lorne.getUserId())); + + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意").specify(dept.getUserId())); + // 撤销流程 flowService.recall(userTodo.getId(), user); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); assertEquals(1, userTodos.size()); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意").specify(dept.getUserId())); // 查看部门经理的待办 List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); @@ -799,7 +795,7 @@ void recallTest() { assertEquals(1, userTodos.size()); userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意").specify(dept.getUserId())); deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); assertEquals(1, deptTodos.size()); @@ -814,7 +810,61 @@ void recallTest() { flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(6, records.size()); + assertEquals(5, records.size()); + + + } + + + /** + * 撤销流程测试 + */ + @Test + void recallTest2() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 撤销流程 + FlowRecord userTodo = userTodos.get(0); + flowService.recall(userTodo.getId(), user); + + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(0, records.size()); } diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java index ecc58e5c..7321f63c 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java @@ -98,13 +98,10 @@ void flowTest() { flowService.submitFlow(deptTodo.getId(), lorne, leave, Opinion.pass("转交给领导审批通过")); bossTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent(); - assertEquals(1, bossTodos.size()); - - bossTodo = bossTodos.get(0); - flowService.submitFlow(bossTodo.getId(), lorne, leave, Opinion.pass("领导审批通过")); + assertEquals(0, bossTodos.size()); List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(6, records.size()); + assertEquals(5, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/MultiRelationFlowTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/MultiRelationFlowTest.java index f9d09469..dea70b23 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/MultiRelationFlowTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/MultiRelationFlowTest.java @@ -55,7 +55,7 @@ void relationTest1(){ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "over",new OutTrigger("def run(content){content.getBindData().getDays()<=5}"),1,false) @@ -91,23 +91,20 @@ void relationTest1(){ // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(3, records.size()); + assertEquals(2, records.size()); // 最终用户确认 userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); + assertEquals(0, userTodos.size()); - // 提交流程 - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(3, records.size()); + assertEquals(2, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(4, snapshots.size()); + assertEquals(3, snapshots.size()); } @@ -138,7 +135,7 @@ void relationTest2(){ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "over",new OutTrigger("def run(content){content.getBindData().getDays()<=5}"),1,false) @@ -183,24 +180,21 @@ void relationTest2(){ // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); // 最终用户确认 userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); + assertEquals(0, userTodos.size()); - // 提交流程 - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(5, snapshots.size()); + assertEquals(4, snapshots.size()); } @@ -229,7 +223,7 @@ void relationTest3(){ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "over",new OutTrigger("def run(content){content.getBindData().getDays()<=5}"),1,false) @@ -299,16 +293,12 @@ void relationTest3(){ // 用户修改确认 userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); - - // 提交流程 - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(0, userTodos.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(7, snapshots.size()); + assertEquals(6, snapshots.size()); } } diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/QueryTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/QueryTest.java index d975ec01..b4da2889 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/QueryTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/QueryTest.java @@ -54,7 +54,7 @@ void queryUserToDo(){ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") @@ -97,21 +97,18 @@ void queryUserToDo(){ // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); - - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(0, userTodos.size()); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(5, snapshots.size()); + assertEquals(4, snapshots.size()); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); assertEquals(0, userTodos.size()); @@ -148,7 +145,7 @@ void queryUserTimeoutTodo(){ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher(),100,true) .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") @@ -202,21 +199,18 @@ void queryUserTimeoutTodo(){ // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent();; - assertEquals(4, records.size()); + assertEquals(3, records.size()); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); - - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(0, userTodos.size()); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(5, snapshots.size()); + assertEquals(4, snapshots.size()); userTodos = flowRecordRepository.findTimeoutTodoByOperatorId(user.getUserId(), pageRequest).getContent(); assertEquals(0, userTodos.size()); @@ -254,7 +248,7 @@ void queryUserPostponedTodo(){ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher(),100,true) .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") @@ -314,21 +308,19 @@ void queryUserPostponedTodo(){ // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); + assertEquals(0, userTodos.size()); - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(5, snapshots.size()); + assertEquals(4, snapshots.size()); userTodos = flowRecordRepository.findPostponedTodoByOperatorId(user.getUserId(), pageRequest).getContent(); assertEquals(0, userTodos.size()); @@ -366,7 +358,7 @@ void queryUserDone(){ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") @@ -409,29 +401,26 @@ void queryUserDone(){ // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); - - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(0, userTodos.size()); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); FlowDetail flowDetail = flowService.detail(records.get(0).getId(), user); - assertEquals(4, flowDetail.getHistoryRecords().size()); - assertEquals(4, flowDetail.getOpinions().size()); + assertEquals(3, flowDetail.getHistoryRecords().size()); + assertEquals(3, flowDetail.getOpinions().size()); List snapshots = flowBindDataRepository.findAll(); - assertEquals(5, snapshots.size()); + assertEquals(4, snapshots.size()); List userDones = flowRecordRepository.findDoneByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(2, userDones.size()); + assertEquals(1, userDones.size()); List deptDones = flowRecordRepository.findDoneByOperatorId(dept.getUserId(), pageRequest).getContent(); assertEquals(1, deptDones.size()); @@ -439,6 +428,7 @@ void queryUserDone(){ List bossDones = flowRecordRepository.findDoneByOperatorId(boss.getUserId(), pageRequest).getContent(); assertEquals(1, bossDones.size()); + assertTrue(bossDones.stream().allMatch(FlowRecord::isRead)); } @@ -466,7 +456,7 @@ void queryUserInitiated(){ .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") @@ -509,21 +499,18 @@ void queryUserInitiated(){ // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); - - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(0, userTodos.size()); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(4, records.size()); + assertEquals(3, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(5, snapshots.size()); + assertEquals(4, snapshots.size()); diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptBuildTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptBuildTest.java index ce99c991..e41a359a 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptBuildTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptBuildTest.java @@ -6,7 +6,8 @@ import com.codingapi.springboot.flow.user.User; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; public class ScriptBuildTest { @@ -14,14 +15,14 @@ public class ScriptBuildTest { @Test void copy() { User user = new User("张三"); - String script = "{\"nodes\":[{\"id\":\"e7699cab-e20b-4606-af66-fb8d782fd0f8\",\"type\":\"start-node\",\"x\":1005,\"y\":153,\"properties\":{\"name\":\"开始节点\",\"code\":\"start\",\"type\":\"START\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCreateOperator().getUserId()];}\",\"editable\":true,\"titleGenerator\":\"def run(content){ return content.getCreateOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"UN_SIGN\",\"timeout\":0,\"id\":\"e7699cab-e20b-4606-af66-fb8d782fd0f8\",\"width\":200,\"height\":45,\"operatorMatcherType\":\"creator\",\"titleGeneratorType\":\"default\",\"errTriggerType\":\"custom\"}},{\"id\":\"0d6638e9-0f98-45de-a5ad-8d55702158f7\",\"type\":\"node-node\",\"x\":722,\"y\":392,\"properties\":{\"name\":\"部门审批\",\"code\":\"dept\",\"type\":\"APPROVAL\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCurrentOperator().getUserId()];}\",\"editable\":true,\"titleGenerator\":\"def run(content){ return content.getCreateOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"SIGN\",\"timeout\":0,\"id\":\"0d6638e9-0f98-45de-a5ad-8d55702158f7\",\"width\":200,\"height\":45}},{\"id\":\"c4f8dd1b-53f2-4a87-8f57-9828b79fc4d0\",\"type\":\"over-node\",\"x\":918,\"y\":773,\"properties\":{\"name\":\"结束节点\",\"code\":\"over\",\"type\":\"OVER\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCurrentOperator().getUserId()];}\",\"editable\":true,\"titleGenerator\":\"def run(content){ return content.getCreateOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"UN_SIGN\",\"timeout\":0,\"id\":\"c4f8dd1b-53f2-4a87-8f57-9828b79fc4d0\",\"width\":200,\"height\":45}},{\"id\":\"ed654f48-c94c-4fdd-9b14-91f6295ae17a\",\"type\":\"node-node\",\"x\":1227,\"y\":530,\"properties\":{\"name\":\"老板审批\",\"code\":\"boss\",\"type\":\"APPROVAL\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCurrentOperator().getUserId()];}\",\"editable\":true,\"titleGenerator\":\"def run(content){ return content.getCreateOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"SIGN\",\"timeout\":0,\"id\":\"ed654f48-c94c-4fdd-9b14-91f6295ae17a\",\"width\":200,\"height\":45}}],\"edges\":[{\"id\":\"6854a4ef-89cf-48d3-9aa7-af1e5b87e93c\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":2,\"back\":false},\"sourceNodeId\":\"e7699cab-e20b-4606-af66-fb8d782fd0f8\",\"targetNodeId\":\"0d6638e9-0f98-45de-a5ad-8d55702158f7\",\"startPoint\":{\"x\":1005,\"y\":175.5},\"endPoint\":{\"x\":722,\"y\":369.5},\"pointsList\":[{\"x\":1005,\"y\":175.5},{\"x\":1005,\"y\":275.5},{\"x\":722,\"y\":269.5},{\"x\":722,\"y\":369.5}]},{\"id\":\"7c2c01fc-ded1-46e7-baf7-9fa664898d74\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":1,\"back\":false},\"sourceNodeId\":\"0d6638e9-0f98-45de-a5ad-8d55702158f7\",\"targetNodeId\":\"c4f8dd1b-53f2-4a87-8f57-9828b79fc4d0\",\"startPoint\":{\"x\":722,\"y\":414.5},\"endPoint\":{\"x\":918,\"y\":750.5},\"pointsList\":[{\"x\":722,\"y\":414.5},{\"x\":722,\"y\":514.5},{\"x\":918,\"y\":650.5},{\"x\":918,\"y\":750.5}]},{\"id\":\"67ee0fe7-b88c-4fde-be82-e23276f567e6\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":1,\"back\":false},\"sourceNodeId\":\"e7699cab-e20b-4606-af66-fb8d782fd0f8\",\"targetNodeId\":\"ed654f48-c94c-4fdd-9b14-91f6295ae17a\",\"startPoint\":{\"x\":1005,\"y\":175.5},\"endPoint\":{\"x\":1227,\"y\":507.5},\"pointsList\":[{\"x\":1005,\"y\":175.5},{\"x\":1005,\"y\":275.5},{\"x\":1227,\"y\":407.5},{\"x\":1227,\"y\":507.5}]},{\"id\":\"9c6f3b0c-03f5-46eb-be39-c79528a824dc\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":1,\"back\":false},\"sourceNodeId\":\"ed654f48-c94c-4fdd-9b14-91f6295ae17a\",\"targetNodeId\":\"c4f8dd1b-53f2-4a87-8f57-9828b79fc4d0\",\"startPoint\":{\"x\":1227,\"y\":552.5},\"endPoint\":{\"x\":918,\"y\":750.5},\"pointsList\":[{\"x\":1227,\"y\":552.5},{\"x\":1227,\"y\":652.5},{\"x\":918,\"y\":650.5},{\"x\":918,\"y\":750.5}]}]}"; + String script = "{\"nodes\":[{\"id\":\"b82a84e7-2c1d-4e15-a3c5-6f7f6e263acd\",\"type\":\"start-node\",\"x\":593,\"y\":96,\"properties\":{\"name\":\"开始节点\",\"code\":\"start\",\"type\":\"START\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCurrentOperator().getUserId()];}\",\"editable\":true,\"titleGenerator\":\"def run(content){ return content.getCurrentOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"UN_SIGN\",\"timeout\":0,\"id\":\"b82a84e7-2c1d-4e15-a3c5-6f7f6e263acd\",\"width\":200,\"height\":45,\"operatorMatcherType\":\"any\",\"titleGeneratorType\":\"default\",\"errTriggerType\":\"custom\"}},{\"id\":\"3c2c420a-003b-4f51-9489-3cdcda0bbe35\",\"type\":\"node-node\",\"x\":620,\"y\":239,\"properties\":{\"name\":\"流程节点\",\"code\":\"flow\",\"type\":\"APPROVAL\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCurrentOperator().getUserId()];}\",\"editable\":true,\"titleGenerator\":\"def run(content){ return content.getCurrentOperator().getName() + '8899-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"SIGN\",\"timeout\":10,\"id\":\"3c2c420a-003b-4f51-9489-3cdcda0bbe35\",\"width\":200,\"height\":45,\"operatorMatcherType\":\"any\",\"titleGeneratorType\":\"custom\",\"errTriggerType\":\"custom\"}},{\"id\":\"b527b4a5-f11f-4052-9848-2c0426da970c\",\"type\":\"over-node\",\"x\":828,\"y\":582,\"properties\":{\"name\":\"结束节点\",\"code\":\"over\",\"type\":\"OVER\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCurrentOperator().getUserId()];}\",\"editable\":true,\"titleGenerator\":\"def run(content){ return content.getCurrentOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"UN_SIGN\",\"timeout\":0,\"id\":\"b527b4a5-f11f-4052-9848-2c0426da970c\",\"width\":200,\"height\":45,\"operatorMatcherType\":\"any\",\"titleGeneratorType\":\"default\",\"errTriggerType\":\"custom\"}},{\"id\":\"2ecdb8aa-00b2-42af-b3ed-c776d2431b38\",\"type\":\"circulate-node\",\"x\":839,\"y\":409,\"properties\":{\"name\":\"抄送节点\",\"code\":\"circulate\",\"type\":\"CIRCULATE\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCreateOperator().getUserId()];}\",\"editable\":true,\"titleGenerator\":\"def run(content){ return content.getCurrentOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"CIRCULATE\",\"timeout\":0,\"id\":\"2ecdb8aa-00b2-42af-b3ed-c776d2431b38\",\"width\":200,\"height\":45}}],\"edges\":[{\"id\":\"b68837fb-dca8-41d2-908c-dc079a7f61de\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":1,\"back\":false},\"sourceNodeId\":\"b82a84e7-2c1d-4e15-a3c5-6f7f6e263acd\",\"targetNodeId\":\"3c2c420a-003b-4f51-9489-3cdcda0bbe35\",\"startPoint\":{\"x\":593,\"y\":118.5},\"endPoint\":{\"x\":620,\"y\":216.5},\"pointsList\":[{\"x\":593,\"y\":118.5},{\"x\":593,\"y\":218.5},{\"x\":620,\"y\":116.5},{\"x\":620,\"y\":216.5}]},{\"id\":\"73e04b95-50f6-44cc-a960-d3007d27fd48\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":2,\"back\":false},\"sourceNodeId\":\"3c2c420a-003b-4f51-9489-3cdcda0bbe35\",\"targetNodeId\":\"2ecdb8aa-00b2-42af-b3ed-c776d2431b38\",\"startPoint\":{\"x\":720,\"y\":239},\"endPoint\":{\"x\":739,\"y\":409},\"pointsList\":[{\"x\":720,\"y\":239},{\"x\":820,\"y\":239},{\"x\":639,\"y\":409},{\"x\":739,\"y\":409}]},{\"id\":\"f6929c79-b168-4c3c-9f8f-9dc21fcaf29d\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":1,\"back\":false},\"sourceNodeId\":\"2ecdb8aa-00b2-42af-b3ed-c776d2431b38\",\"targetNodeId\":\"b527b4a5-f11f-4052-9848-2c0426da970c\",\"startPoint\":{\"x\":839,\"y\":431.5},\"endPoint\":{\"x\":828,\"y\":559.5},\"pointsList\":[{\"x\":839,\"y\":431.5},{\"x\":839,\"y\":531.5},{\"x\":828,\"y\":459.5},{\"x\":828,\"y\":559.5}]}]}"; FlowWork flowWork = FlowWorkBuilder.builder(user) .title("请假流程") .schema(script) .build(); assertEquals("请假流程", flowWork.getTitle()); assertEquals(4, flowWork.getNodes().size()); - assertEquals(4, flowWork.getRelations().size()); + assertEquals(3, flowWork.getRelations().size()); FlowNode startNode = flowWork.getStartNode(); @@ -31,7 +32,7 @@ void copy() { assertEquals("请假流程", copyWork.getTitle()); assertEquals(4, copyWork.getNodes().size()); - assertEquals(4, copyWork.getRelations().size()); + assertEquals(3, copyWork.getRelations().size()); copyWork.verify(); diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptTest.java index d2f9d9e7..7f1abf00 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptTest.java @@ -52,7 +52,7 @@ void test() { long now = System.currentTimeMillis(); Leave leave = new Leave("我要请假"); - FlowSession flowSession = new FlowSession(flowWork, flowWork.getNodeByCode("start"), user, user, leave, Opinion.pass("同意"),new ArrayList<>()); + FlowSession flowSession = new FlowSession(null,flowWork, flowWork.getNodeByCode("start"), user, user, leave, Opinion.pass("同意"),new ArrayList<>()); List ids = matcher.matcher(flowSession); assertTrue(ids.contains(user.getUserId())); diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/SignTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/SignTest.java index d5128b83..c0a02b51 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/SignTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/SignTest.java @@ -61,7 +61,7 @@ void unSignTest(){ .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId(),caocao.getUserId(),lvBu.getUserId(),zhaoYun.getUserId())) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") @@ -104,21 +104,18 @@ void unSignTest(){ // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent();; - assertEquals(7, records.size()); + assertEquals(6, records.size()); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); - - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(0, userTodos.size()); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(7, records.size()); + assertEquals(6, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(5, snapshots.size()); + assertEquals(4, snapshots.size()); } @@ -154,7 +151,7 @@ void signTest(){ .node("部门领导审批", "dept", "default", ApprovalType.SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId(),caocao.getUserId(),lvBu.getUserId(),zhaoYun.getUserId())) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") @@ -226,21 +223,18 @@ void signTest(){ // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(7, records.size()); + assertEquals(6, records.size()); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); - - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(0, userTodos.size()); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(7, records.size()); + assertEquals(6, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(8, snapshots.size()); + assertEquals(7, snapshots.size()); } @@ -283,7 +277,7 @@ void signRejectTest(){ zhaoYun.getUserId() )) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) - .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.creatorOperatorMatcher()) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("部门领导审批", "start", "dept") .relation("总经理审批", "dept", "manager") @@ -395,21 +389,18 @@ void signRejectTest(){ // 查看所有流程 List records = flowRecordRepository.findAll(pageRequest).getContent();; - assertEquals(12, records.size()); + assertEquals(11, records.size()); userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); - assertEquals(1, userTodos.size()); - - userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(0, userTodos.size()); records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(12, records.size()); + assertEquals(11, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); List snapshots = flowBindDataRepository.findAll(); - assertEquals(13, snapshots.size()); + assertEquals(12, snapshots.size()); } } diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/TrySubmitTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/TrySubmitTest.java new file mode 100644 index 00000000..594201d6 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/TrySubmitTest.java @@ -0,0 +1,134 @@ +package com.codingapi.springboot.flow.test; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.build.FlowWorkBuilder; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.flow.Leave; +import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.pojo.FlowSubmitResult; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.*; +import com.codingapi.springboot.flow.service.FlowService; +import com.codingapi.springboot.flow.user.User; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TrySubmitTest { + + private final UserRepository userRepository = new UserRepository(); + private final FlowWorkRepository flowWorkRepository = new FlowWorkRepositoryImpl(); + private final FlowRecordRepositoryImpl flowRecordRepository = new FlowRecordRepositoryImpl(); + private final FlowBindDataRepositoryImpl flowBindDataRepository = new FlowBindDataRepositoryImpl(); + private final LeaveRepository leaveRepository = new LeaveRepository(); + private final FlowBackupRepository flowBackupRepository = new FlowBackupRepositoryImpl(); + private final FlowProcessRepository flowProcessRepository = new FlowProcessRepositoryImpl(flowBackupRepository,userRepository); + private final FlowService flowService = new FlowService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, userRepository,flowProcessRepository,flowBackupRepository); + + /** + * 预提交测试 + */ + @Test + void test() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User lorne = new User("lorne"); + userRepository.save(lorne); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备", lorne); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + FlowSubmitResult flowSubmitResult = flowService.trySubmitFlow(workCode, user, leave, Opinion.pass("发起流程")); + // 下级审批人有1个 + assertEquals(1, flowSubmitResult.getOperators().size()); + // 下级审批人是刘备 + assertEquals(dept.getUserId(), flowSubmitResult.getOperators().get(0).getUserId()); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + + flowSubmitResult = flowService.trySubmitFlow(userTodos.get(0).getId(), user, leave, Opinion.pass("同意")); + // 下级审批人有1个 + assertEquals(1, flowSubmitResult.getOperators().size()); + // 下级审批人是刘备 + assertEquals(dept.getUserId(), flowSubmitResult.getOperators().get(0).getUserId()); + + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看刘备经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(0, deptTodos.size()); + + List lorneTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent(); + assertEquals(1, lorneTodos.size()); + + // 提交委托lorne部门经理的审批 + FlowRecord lorneTodo = lorneTodos.get(0); + flowService.submitFlow(lorneTodo.getId(), lorne, leave, Opinion.pass("同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(3, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(0, userTodos.size()); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(3, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(4, snapshots.size()); + + } + +} diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 62c748bd..a29f9191 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.6 + 2.9.7 springboot-starter-security diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java index c0ecc62b..b7fa3f61 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java @@ -73,7 +73,7 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR LoginRequest loginRequest = LoginRequestContext.getInstance().get(); Token token = tokenGateway.create(user.getUsername(), loginRequest.getPassword(), - user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()), + user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.collect(Collectors.toList())), TokenContext.getExtra()); LoginResponse loginResponse = loginHandler.postHandle(request, response, loginRequest, user, token); diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index e1440db2..085b55f7 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.6 + 2.9.7 springboot-starter From ed2bc28c4d9c22c87719317ccd14313ce174a490 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Thu, 5 Dec 2024 23:55:33 +0800 Subject: [PATCH 063/101] update flow --- .../com/codingapi/springboot/security/filter/MyLoginFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java index b7fa3f61..c0ecc62b 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/filter/MyLoginFilter.java @@ -73,7 +73,7 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR LoginRequest loginRequest = LoginRequestContext.getInstance().get(); Token token = tokenGateway.create(user.getUsername(), loginRequest.getPassword(), - user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.collect(Collectors.toList())), + user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()), TokenContext.getExtra()); LoginResponse loginResponse = loginHandler.postHandle(request, response, loginRequest, user, token); From 84d96a9e0a06e41fb9da51bb221f55b6dd9b6ff6 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Thu, 5 Dec 2024 23:56:49 +0800 Subject: [PATCH 064/101] update flow --- .../springboot/fast/jpa/repository/DynamicSQLBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java index eb2a15c8..caa1a9d7 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java @@ -45,7 +45,7 @@ public String getHQL() { Sort sort = request.getSort(); if (sort.isSorted()) { hql.append(" ORDER BY "); - List orders = sort.collect(Collectors.toList()); + List orders = sort.toList(); for (int i = 0; i < orders.size(); i++) { Sort.Order order = orders.get(i); hql.append(order.getProperty()).append(" ").append(order.getDirection().name()); From 004a90e9b5b39955259511e14209dfc1d6fe6f1b Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Fri, 6 Dec 2024 14:58:30 +0800 Subject: [PATCH 065/101] add not equal --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- .../jpa/repository/DynamicSQLBuilder.java | 7 +++++++ .../springboot/fast/DemoRepositoryTest.java | 19 +++++++++++++++++++ springboot-starter-flow/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../framework/dto/request/Filter.java | 4 ++++ .../framework/dto/request/Relation.java | 1 + .../framework/dto/request/SearchRequest.java | 12 +++++++----- 10 files changed, 43 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index f9d62149..d6657389 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.9.7 + 2.9.8 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index b2efbee5..f9d3a1bc 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.7 + 2.9.8 4.0.0 diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java index caa1a9d7..68052924 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java @@ -98,6 +98,13 @@ private void buildSQL(Filter filter, StringBuilder hql) { params.add(filter.getValue()[0]); paramIndex++; } + + if (filter.isNotEqual()) { + hql.append(filter.getKey()).append(" != ?").append(paramIndex); + params.add(filter.getValue()[0]); + paramIndex++; + } + if (filter.isLike()) { hql.append(filter.getKey()).append(" LIKE ?").append(paramIndex); params.add("%" + filter.getValue()[0] + "%"); diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java index cb2ea09b..236f1d51 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java @@ -56,6 +56,25 @@ void findAll() { assertEquals(1, page.getTotalElements()); } + @Test + void pageRequestNotEqual() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demo1 = demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demo2.setName("456"); + demoRepository.save(demo2); + + PageRequest request = new PageRequest(); + request.setCurrent(1); + request.setPageSize(10); + request.addFilter("id", Relation.NOT_EQUAL, demo1.getId()); + + Page page = demoRepository.pageRequest(request); + assertEquals(1, page.getTotalElements()); + } @Test void pageRequest() { diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 4aac3f3a..dee247fc 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.7 + 2.9.8 springboot-starter-flow diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index a29f9191..a0927f82 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.7 + 2.9.8 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 085b55f7..2d32b141 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.7 + 2.9.8 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java index 723448d5..319a916a 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java @@ -48,6 +48,10 @@ public boolean isEqual() { return relation == Relation.EQUAL; } + public boolean isNotEqual() { + return relation == Relation.NOT_EQUAL; + } + public boolean isLike() { return relation == Relation.LIKE; } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Relation.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Relation.java index 7941a3c4..0bd900dd 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Relation.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Relation.java @@ -2,6 +2,7 @@ public enum Relation { + NOT_EQUAL, EQUAL, LIKE, LEFT_LIKE, diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java index f559812b..f14539ae 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/SearchRequest.java @@ -224,11 +224,13 @@ public PageRequest toPageRequest(Class clazz) { if (JSON.isValid(filter)) { removeKeys.add("filter"); JSONObject jsonObject = JSON.parseObject(filter); - for (String key : jsonObject.keySet()) { - JSONArray value = jsonObject.getJSONArray(key); - if (value != null && !value.isEmpty()) { - List values = value.stream().map(Object::toString).collect(Collectors.toList()); - content.addFilter(key, values); + if(jsonObject!=null) { + for (String key : jsonObject.keySet()) { + JSONArray value = jsonObject.getJSONArray(key); + if (value != null && !value.isEmpty()) { + List values = value.stream().map(Object::toString).collect(Collectors.toList()); + content.addFilter(key, values); + } } } } From 06df1b29c6416e0fa32d44c3d7fa78562c854397 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Fri, 6 Dec 2024 22:12:10 +0800 Subject: [PATCH 066/101] fix trySubmitFlow bug --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../springboot/flow/domain/FlowNode.java | 22 ++- .../flow/matcher/OperatorMatcher.java | 11 ++ .../flow/service/FlowDirectionService.java | 7 +- .../flow/service/impl/FlowSubmitService.java | 4 +- .../service/impl/FlowTrySubmitService.java | 10 +- .../springboot/flow/test/SignTest.java | 141 ++++++++++++++++++ springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- 11 files changed, 184 insertions(+), 21 deletions(-) diff --git a/pom.xml b/pom.xml index d6657389..1afb88ba 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.9.8 + 2.9.9 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index f9d3a1bc..c08748f4 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.8 + 2.9.9 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index dee247fc..c63624fb 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.8 + 2.9.9 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java index 28342d75..37bc95ec 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java @@ -19,7 +19,9 @@ import lombok.Setter; import org.springframework.util.StringUtils; +import java.util.Comparator; import java.util.List; +import java.util.stream.Collectors; /** * 流程节点 @@ -101,21 +103,30 @@ public class FlowNode { */ private List buttons; + /** + * 按钮顺序 + */ + public List getButtons() { + if (buttons != null) { + return buttons.stream().sorted(Comparator.comparingInt(FlowButton::getOrder)).collect(Collectors.toList()); + } + return null; + } - public void verify(){ + public void verify() { if (this.titleGenerator == null) { throw new IllegalArgumentException("titleGenerator is null"); } if (this.operatorMatcher == null) { throw new IllegalArgumentException("operatorMatcher is null"); } - if(timeout<0){ + if (timeout < 0) { throw new IllegalArgumentException("timeout is less than 0"); } - if(!StringUtils.hasLength(id)){ + if (!StringUtils.hasLength(id)) { throw new IllegalArgumentException("id is empty"); } - if(!StringUtils.hasLength(code)){ + if (!StringUtils.hasLength(code)) { throw new IllegalArgumentException("code is empty"); } } @@ -206,8 +217,7 @@ public FlowRecord createRecord(long workId, String title, IFlowOperator createOperator, IFlowOperator currentOperator, - BindDataSnapshot snapshot - ) { + BindDataSnapshot snapshot) { // 当前操作者存在委托人时,才需要寻找委托人 IFlowOperator flowOperator = currentOperator; diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/matcher/OperatorMatcher.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/matcher/OperatorMatcher.java index 950f950e..9580251f 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/matcher/OperatorMatcher.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/matcher/OperatorMatcher.java @@ -85,6 +85,17 @@ public static OperatorMatcher specifyOperatorMatcher(long... userIds) { return new OperatorMatcher("def run(content) {return [" + userIdsStr + "];}", STATE_SPECIFY); } + /** + * 指定操作者匹配器 + * + * @param userIds 用户ids + * @return 操作者匹配器 + */ + public static OperatorMatcher specifyOperatorMatcher(List userIds) { + String userIdsStr = userIds.stream().map(String::valueOf).collect(Collectors.joining(",")); + return new OperatorMatcher("def run(content) {return [" + userIdsStr + "];}", STATE_SPECIFY); + } + /** * 创建者操作者匹配器 * diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java index 3b8f3aac..829238bb 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java @@ -81,12 +81,7 @@ public void verifyFlowSourceDirection() { */ public boolean hasCurrentFlowNodeIsDone() { // 会签下所有人尚未提交时,不执行下一节点 - boolean allDone = historyRecords.stream().filter(item -> !item.isTransfer()).allMatch(FlowRecord::isDone); - if (!allDone) { - // 流程尚未审批结束直接退出 - return true; - } - return false; + return historyRecords.stream().filter(item -> !item.isTransfer()).allMatch(FlowRecord::isDone); } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java index 77d379c6..3f1b1136 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java @@ -98,8 +98,8 @@ public FlowResult submitFlow(long recordId, IFlowOperator currentOperator, IBind // 判断流程是否结束(会签时需要所有人都通过) if (flowNode.isSign()) { - boolean next = flowDirectionService.hasCurrentFlowNodeIsDone(); - if (next) { + boolean isDone = flowDirectionService.hasCurrentFlowNodeIsDone(); + if (!isDone) { List todoRecords = historyRecords.stream().filter(FlowRecord::isTodo).collect(Collectors.toList()); return new FlowResult(flowWork, todoRecords); } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTrySubmitService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTrySubmitService.java index c48fab7f..d80ed9f4 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTrySubmitService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTrySubmitService.java @@ -108,13 +108,19 @@ private FlowSubmitResult trySubmitFlow(FlowWork flowWork, FlowNode flowNode, Flo } else { // copy 流程数据防止影响原有数据 historyRecords = flowRecordRepository.findFlowRecordByPreId(flowRecord.getPreId()).stream().map(FlowRecord::copy).collect(Collectors.toList()); + // 更新当前流程记录, 由于try测试过程中没有对数据落库,所以这里需要手动更新 + for(FlowRecord record : historyRecords){ + if(record.getId() == flowRecord.getId()){ + record.submitRecord(currentOperator, snapshot, opinion, flowSourceDirection); + } + } } flowDirectionService.bindHistoryRecords(historyRecords); // 判断流程是否结束(会签时需要所有人都通过) if (flowNode.isSign()) { - boolean next = flowDirectionService.hasCurrentFlowNodeIsDone(); - if (next) { + boolean isDone = flowDirectionService.hasCurrentFlowNodeIsDone(); + if (!isDone) { List todoRecords = historyRecords.stream().filter(FlowRecord::isTodo).collect(Collectors.toList()); return new FlowSubmitResult(flowWork, flowNode, todoRecords.stream().map(FlowRecord::getCurrentOperator).collect(Collectors.toList())); } diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/SignTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/SignTest.java index c0a02b51..c4073ceb 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/SignTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/SignTest.java @@ -7,14 +7,18 @@ import com.codingapi.springboot.flow.em.ApprovalType; import com.codingapi.springboot.flow.flow.Leave; import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.pojo.FlowSubmitResult; import com.codingapi.springboot.flow.record.FlowRecord; import com.codingapi.springboot.flow.repository.*; import com.codingapi.springboot.flow.service.FlowService; +import com.codingapi.springboot.flow.user.IFlowOperator; import com.codingapi.springboot.flow.user.User; import org.junit.jupiter.api.Test; import org.springframework.data.domain.PageRequest; +import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -403,4 +407,141 @@ void signRejectTest(){ assertEquals(12, snapshots.size()); } + + + + /** + * 多人会签trySubmit测试 + */ + @Test + void signTrySubmitTest(){ + PageRequest pageRequest = PageRequest.of(0, 1000); + + User caocao = new User("曹操"); + userRepository.save(caocao); + User lvBu = new User("吕布"); + userRepository.save(lvBu); + User zhaoYun = new User("赵云"); + userRepository.save(zhaoYun); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + List signUsers = new ArrayList<>(); + signUsers.add(dept); + signUsers.add(caocao); + signUsers.add(lvBu); + signUsers.add(zhaoYun); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.SIGN, OperatorMatcher.specifyOperatorMatcher(signUsers.stream().map(User::getUserId).collect(Collectors.toList()))) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN,OperatorMatcher.specifyOperatorMatcher(boss.getUserId()) ) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + + // 验证会签的人员 + FlowSubmitResult submitResult = flowService.trySubmitFlow(userTodo.getId(), user, leave, Opinion.pass("用户同意")); + List operators = submitResult.getOperators(); + assertEquals(signUsers.size(), operators.size()); + + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("用户同意").specify(dept.getUserId())); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + + // 验证会签的人员 + submitResult = flowService.trySubmitFlow(deptTodo.getId(), dept, leave, Opinion.pass("用户同意")); + operators = submitResult.getOperators(); + assertEquals(1, operators.size()); + assertEquals(boss.getName(), operators.get(0).getName()); + + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("刘备同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + +// // 查看部门经理 吕布 的待办 +// List lvbuTodos = flowRecordRepository.findTodoByOperatorId(lvBu.getUserId(), pageRequest).getContent(); +// assertEquals(1, lvbuTodos.size()); +// +// // 提交部门经理 吕布 的审批 +// FlowRecord lvbuTodo = lvbuTodos.get(0); +// flowService.submitFlow(lvbuTodo.getId(), lvBu, leave, Opinion.pass("吕布同意")); +// +// +// // 查看部门经理 赵云 的待办 +// List zhaoYunTodos = flowRecordRepository.findTodoByOperatorId(zhaoYun.getUserId(), pageRequest).getContent(); +// assertEquals(1, zhaoYunTodos.size()); +// +// // 提交部门经理 赵云 的审批 +// FlowRecord zhaoYunTodo = zhaoYunTodos.get(0); +// flowService.submitFlow(zhaoYunTodo.getId(), zhaoYun, leave, Opinion.pass("赵云同意")); +// +// +// // 查看部门经理 曹操 的待办 +// List caocaoTodos = flowRecordRepository.findTodoByOperatorId(caocao.getUserId(), pageRequest).getContent(); +// assertEquals(1, caocaoTodos.size()); +// +// // 提交部门经理 曹操 的审批 +// FlowRecord caocaoTodo = caocaoTodos.get(0); +// flowService.submitFlow(caocaoTodo.getId(), caocao, leave, Opinion.pass("曹操同意")); +// +// bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); +// assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(3, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(0, userTodos.size()); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(3, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(4, snapshots.size()); + + } } diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index a0927f82..6da8d47d 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.8 + 2.9.9 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 2d32b141..8defe7d2 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.8 + 2.9.9 springboot-starter From cdd6060d905fd0e8db2e32af85ea7b51526a1d20 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sat, 7 Dec 2024 15:41:01 +0800 Subject: [PATCH 067/101] add springboot-parent --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../springboot/flow/content/FlowSession.java | 13 +++++++++++++ .../codingapi/springboot/flow/pojo/FlowDetail.java | 4 ++++ springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- 7 files changed, 22 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 1afb88ba..5ad4e087 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.9.9 + 2.9.10 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index c08748f4..d72e4f33 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.9 + 2.9.10 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index c63624fb..393787de 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.9 + 2.9.10 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java index be9cf44e..515d42d9 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java @@ -65,6 +65,19 @@ public Object getBean(String beanName) { return provider.getBean(beanName); } + + + /** + * 获取审批意见 + */ + public String getAdvice() { + if (opinion != null) { + return opinion.getAdvice(); + } else { + return null; + } + } + /** * 创建节点结果 * diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java index a8f2be0f..1b73841f 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java @@ -103,10 +103,14 @@ public FlowDetail(FlowWork flowWork, public final class FlowOpinion { private final long recordId; private final Opinion opinion; + private final String nodeCode; + private final String nodeName; private final IFlowOperator operator; private final long createTime; public FlowOpinion(FlowRecord flowRecord) { + this.nodeCode = flowRecord.getNodeCode(); + this.nodeName = flowWork.getNodeByCode(nodeCode).getName(); this.recordId = flowRecord.getId(); this.opinion = flowRecord.getOpinion(); this.operator = flowRecord.getCurrentOperator(); diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 6da8d47d..24e143e5 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.9 + 2.9.10 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 8defe7d2..b68aae75 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.9 + 2.9.10 springboot-starter From 609225bc970fa523e65b1b65adcd8c31cdf2609b Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Mon, 9 Dec 2024 23:36:04 +0800 Subject: [PATCH 068/101] add 2.9.11 --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../com/codingapi/springboot/flow/domain/FlowButton.java | 6 ++++++ springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- 6 files changed, 11 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 5ad4e087..e1075e7e 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.9.10 + 2.9.11 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index d72e4f33..9fb04bc0 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.10 + 2.9.11 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 393787de..f20faf76 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.10 + 2.9.11 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowButton.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowButton.java index 6bd5c440..7dd5d8e5 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowButton.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowButton.java @@ -39,6 +39,12 @@ public class FlowButton { * 自定义事件内容 (后端脚本) */ private String groovy; + + /** + * 自定义事件内容 (前端脚本) + */ + private String eventKey; + /** * 排序 */ diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 24e143e5..be776c43 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.10 + 2.9.11 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index b68aae75..08ed1384 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.10 + 2.9.11 springboot-starter From 3d0eaa626362b2e62ea6261951e150f3da81d660 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Tue, 10 Dec 2024 11:51:43 +0800 Subject: [PATCH 069/101] fix resetToken --- .../security/redis/RedisTokenGateway.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGateway.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGateway.java index d4eb4257..e38c573d 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGateway.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGateway.java @@ -38,10 +38,26 @@ public Token parser(String sign) { return JSONObject.parseObject(json, Token.class); } + /** + * 删除token + * @param token token + */ public void removeToken(String token) { redisTemplate.delete(token); } + /** + * 重置token + * @param token token + */ + public void resetToken(Token token){ + redisTemplate.opsForValue().set(token.getToken(), token.toJson(), validTime, TimeUnit.MILLISECONDS); + } + + /** + * 删除用户 + * @param username 用户名 + */ public void removeUsername(String username) { Set keys = redisTemplate.keys(username + ":*"); if (keys != null && !keys.isEmpty()) { @@ -49,6 +65,11 @@ public void removeUsername(String username) { } } + /** + * 自定义删除用户 + * @param username 用户名 + * @param predicate 条件 + */ public void removeUsername(String username, Predicate predicate) { Set keys = redisTemplate.keys(username + ":*"); if (keys != null && !keys.isEmpty()) { From c2c22883455a531b3ccedfaf137a1e8f91256ac2 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Tue, 10 Dec 2024 11:52:12 +0800 Subject: [PATCH 070/101] fix resetToken --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index e1075e7e..1cb9195f 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.9.11 + 2.9.12 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 9fb04bc0..a400c560 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.11 + 2.9.12 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index f20faf76..861a55c4 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.11 + 2.9.12 springboot-starter-flow diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index be776c43..e2099d17 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.11 + 2.9.12 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 08ed1384..dd76d533 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.11 + 2.9.12 springboot-starter From ab6ef03f9443614853ebdc9326ac08653bd6785e Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Tue, 10 Dec 2024 16:11:12 +0800 Subject: [PATCH 071/101] fix resetToken --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- .../security/redis/RedisTokenGateway.java | 38 +++++++++++++++---- .../security/redis/RedisTokenGatewayImpl.java | 2 +- springboot-starter/pom.xml | 2 +- 7 files changed, 37 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index 1cb9195f..36b24408 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.9.12 + 2.9.13 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index a400c560..9ef218ad 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.12 + 2.9.13 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 861a55c4..a39352ed 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.12 + 2.9.13 springboot-starter-flow diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index e2099d17..68bef439 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.12 + 2.9.13 springboot-starter-security diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGateway.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGateway.java index e38c573d..0e7d69f0 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGateway.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGateway.java @@ -4,6 +4,7 @@ import com.codingapi.springboot.security.gateway.Token; import org.springframework.data.redis.core.RedisTemplate; +import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.UUID; @@ -30,8 +31,14 @@ public Token create(String username, String iv, List authorities, String return token; } - public Token parser(String sign) { - String json = redisTemplate.opsForValue().get(sign); + /** + * 根据token获取用户信息 + * + * @param token token + * @return 用户信息 + */ + public Token getToken(String token) { + String json = redisTemplate.opsForValue().get(token); if (json == null) { return null; } @@ -40,6 +47,7 @@ public Token parser(String sign) { /** * 删除token + * * @param token token */ public void removeToken(String token) { @@ -48,33 +56,49 @@ public void removeToken(String token) { /** * 重置token + * * @param token token */ - public void resetToken(Token token){ + public void resetToken(Token token) { redisTemplate.opsForValue().set(token.getToken(), token.toJson(), validTime, TimeUnit.MILLISECONDS); } /** * 删除用户 + * * @param username 用户名 */ public void removeUsername(String username) { Set keys = redisTemplate.keys(username + ":*"); - if (keys != null && !keys.isEmpty()) { + if (!keys.isEmpty()) { redisTemplate.delete(keys); } } + /** - * 自定义删除用户 + * 获取用户的所有token + * * @param username 用户名 + * @return token列表 + */ + public List getTokensByUsername(String username) { + Set keys = redisTemplate.keys(username + ":*"); + return new ArrayList<>(keys); + } + + + /** + * 自定义删除用户 + * + * @param username 用户名 * @param predicate 条件 */ public void removeUsername(String username, Predicate predicate) { Set keys = redisTemplate.keys(username + ":*"); - if (keys != null && !keys.isEmpty()) { + if (!keys.isEmpty()) { for (String key : keys) { - Token token = parser(key); + Token token = getToken(key); if (token != null && predicate.test(token)) { redisTemplate.delete(key); } diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGatewayImpl.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGatewayImpl.java index 19453d66..8973f26d 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGatewayImpl.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/redis/RedisTokenGatewayImpl.java @@ -20,7 +20,7 @@ public Token create(String username, String iv, List authorities, String @Override public Token parser(String sign) { - return redisTokenGateway.parser(sign); + return redisTokenGateway.getToken(sign); } } diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index dd76d533..83d8f939 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.12 + 2.9.13 springboot-starter From dfff4a84697924647e4bfbe7803b44c54cb25a1e Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Thu, 12 Dec 2024 09:46:26 +0800 Subject: [PATCH 072/101] add 2.9.14 --- pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../framework/annotation/ColumnType.java | 47 +++++++++++++++++++ .../framework/annotation/MetaColumn.java | 43 +++++++++++++++++ .../framework/annotation/MetaRelation.java | 20 ++++++++ .../framework/annotation/MetaTable.java | 23 +++++++++ .../src/main/resources/META-INF/banner.txt | 4 ++ 10 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/ColumnType.java create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/MetaColumn.java create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/MetaRelation.java create mode 100644 springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/MetaTable.java create mode 100644 springboot-starter/src/main/resources/META-INF/banner.txt diff --git a/pom.xml b/pom.xml index 36b24408..69009d6e 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.codingapi.springboot springboot-parent - 2.9.13 + 2.9.14 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 9ef218ad..8d142d1e 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.13 + 2.9.14 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index a39352ed..b5fadf19 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.13 + 2.9.14 springboot-starter-flow diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 68bef439..0357b2d4 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.13 + 2.9.14 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 83d8f939..d8c0d844 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.13 + 2.9.14 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/ColumnType.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/ColumnType.java new file mode 100644 index 00000000..215814c1 --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/ColumnType.java @@ -0,0 +1,47 @@ +package com.codingapi.springboot.framework.annotation; + +/** + * 数据库字段类型 + */ +public enum ColumnType { + + /** + * 整数 + */ + Number, + + /** + * 浮点数 + */ + Float, + + /** + * 字符串 + */ + String, + + /** + * 日期 + */ + Date, + + /** + * 文件 + */ + File, + + /** + * 布尔 + */ + Boolean, + + /** + * 字节 + */ + Bytes, + + /** + * JSON + */ + JSON, +} diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/MetaColumn.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/MetaColumn.java new file mode 100644 index 00000000..289a1d04 --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/MetaColumn.java @@ -0,0 +1,43 @@ +package com.codingapi.springboot.framework.annotation; + +import java.lang.annotation.*; + +/** + * 查询字段 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface MetaColumn { + + /** + * 字段说明 + */ + String desc(); + + /** + * 字段名称 + */ + String name(); + + /** + * 是否主键 + */ + boolean primaryKey() default false; + + /** + * 字段类型 + */ + ColumnType type() default ColumnType.String; + + /** + * 格式化 + */ + String format() default ""; + + /** + * 依赖表 + */ + MetaRelation dependent() default @MetaRelation(tableName = "", columnName = ""); + +} diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/MetaRelation.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/MetaRelation.java new file mode 100644 index 00000000..2ee40710 --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/MetaRelation.java @@ -0,0 +1,20 @@ +package com.codingapi.springboot.framework.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface MetaRelation { + + /** + * 表名称 + */ + String tableName(); + + /** + * 字段名称 + */ + String columnName(); +} diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/MetaTable.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/MetaTable.java new file mode 100644 index 00000000..6075b1eb --- /dev/null +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/MetaTable.java @@ -0,0 +1,23 @@ +package com.codingapi.springboot.framework.annotation; + +import java.lang.annotation.*; + +/** + * 查询表 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface MetaTable { + + /** + * 表说明 + */ + String desc(); + + /** + * 表名称 + */ + String name(); + +} diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt new file mode 100644 index 00000000..23fb5d83 --- /dev/null +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -0,0 +1,4 @@ +------------------------------------------------------ +CodingApi SpringBoot-Starter 2.9.14 +springboot version (${spring-boot.version}) +------------------------------------------------------ From 6ee0ebe63584b1762acd9ccdfd23db034a740d13 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Thu, 19 Dec 2024 09:59:22 +0800 Subject: [PATCH 073/101] add springboot-starter-data-authorization --- README.md | 8 + pom.xml | 21 +- springboot-starter-data-authorization/pom.xml | 77 ++ .../DataAuthorizationConfiguration.java | 48 + .../DataAuthorizationContext.java | 81 ++ .../enhancer/DataPermissionSQLEnhancer.java | 110 ++ .../enhancer/TableColumnAlias.java | 62 + .../enhancer/TableColumnAliasContext.java | 92 ++ .../enhancer/TableColumnAliasHolder.java | 115 ++ .../exception/NotAuthorizationException.java | 13 + .../filter/DataAuthorizationFilter.java | 44 + .../DefaultDataAuthorizationFilter.java | 27 + .../authorization/handler/ColumnHandler.java | 74 + .../handler/ColumnHandlerContext.java | 144 ++ .../authorization/handler/Condition.java | 33 + .../handler/DefaultColumnHandler.java | 161 +++ .../handler/DefaultRowHandler.java | 11 + .../authorization/handler/RowHandler.java | 19 + .../handler/RowHandlerContext.java | 18 + .../interceptor/DataPermissionSQL.java | 18 + .../interceptor/DefaultSQLInterceptor.java | 35 + .../interceptor/SQLInterceptState.java | 60 + .../interceptor/SQLInterceptor.java | 39 + .../interceptor/SQLInterceptorContext.java | 20 + .../interceptor/SQLRunningContext.java | 67 + .../jdbc/AuthorizationJdbcDriver.java | 57 + .../jdbc/proxy/CallableStatementProxy.java | 1191 +++++++++++++++++ .../jdbc/proxy/ConnectionProxy.java | 300 +++++ .../jdbc/proxy/PreparedStatementProxy.java | 585 ++++++++ .../jdbc/proxy/ResultSetProxy.java | 1180 ++++++++++++++++ .../jdbc/proxy/StatementProxy.java | 290 ++++ .../authorization/mask/ColumnMask.java | 12 + .../authorization/mask/ColumnMaskContext.java | 34 + .../authorization/mask/impl/BankCardMask.java | 43 + .../authorization/mask/impl/IDCardMask.java | 29 + .../authorization/mask/impl/PhoneMask.java | 31 + .../DataAuthorizationProperties.java | 17 + .../DataAuthorizationPropertyContext.java | 24 + .../register/ConditionHandlerRegister.java | 14 + .../DataAuthorizationContextRegister.java | 17 + .../register/ResultSetHandlerRegister.java | 15 + .../register/SQLInterceptorRegister.java | 14 + .../authorization/utils/SQLUtils.java | 25 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + .../DataAuthorizationContextTest.java | 378 ++++++ .../DataAuthorizationTestApplication.java | 13 + .../analyzer/SelectSQLAnalyzerTest.java | 198 +++ .../authorization/current/CurrentUser.java | 27 + .../authorization/entity/Depart.java | 38 + .../springboot/authorization/entity/Unit.java | 34 + .../springboot/authorization/entity/User.java | 49 + .../mask/impl/BankCardMaskTest.java | 17 + .../mask/impl/IDCardMaskTest.java | 16 + .../mask/impl/PhoneMaskTest.java | 17 + .../repository/DepartRepository.java | 8 + .../repository/UnitRepository.java | 8 + .../repository/UserRepository.java | 12 + .../src/test/resources/application.properties | 13 + springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 63 files changed, 6105 insertions(+), 9 deletions(-) create mode 100644 springboot-starter-data-authorization/pom.xml create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/DataAuthorizationConfiguration.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/DataAuthorizationContext.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/DataPermissionSQLEnhancer.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/TableColumnAlias.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/TableColumnAliasContext.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/TableColumnAliasHolder.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/exception/NotAuthorizationException.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/filter/DataAuthorizationFilter.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/filter/DefaultDataAuthorizationFilter.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/ColumnHandler.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/ColumnHandlerContext.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/Condition.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/DefaultColumnHandler.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/DefaultRowHandler.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/RowHandler.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/RowHandlerContext.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/DataPermissionSQL.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/DefaultSQLInterceptor.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLInterceptState.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLInterceptor.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLInterceptorContext.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLRunningContext.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/AuthorizationJdbcDriver.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/CallableStatementProxy.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/ConnectionProxy.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/PreparedStatementProxy.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/ResultSetProxy.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/StatementProxy.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/ColumnMask.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/ColumnMaskContext.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/impl/BankCardMask.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/impl/IDCardMask.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/impl/PhoneMask.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/properties/DataAuthorizationProperties.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/properties/DataAuthorizationPropertyContext.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/register/ConditionHandlerRegister.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/register/DataAuthorizationContextRegister.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/register/ResultSetHandlerRegister.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/register/SQLInterceptorRegister.java create mode 100644 springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/utils/SQLUtils.java create mode 100644 springboot-starter-data-authorization/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/DataAuthorizationContextTest.java create mode 100644 springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/DataAuthorizationTestApplication.java create mode 100644 springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/analyzer/SelectSQLAnalyzerTest.java create mode 100644 springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/current/CurrentUser.java create mode 100644 springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/entity/Depart.java create mode 100644 springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/entity/Unit.java create mode 100644 springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/entity/User.java create mode 100644 springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/mask/impl/BankCardMaskTest.java create mode 100644 springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/mask/impl/IDCardMaskTest.java create mode 100644 springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/mask/impl/PhoneMaskTest.java create mode 100644 springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/repository/DepartRepository.java create mode 100644 springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/repository/UnitRepository.java create mode 100644 springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/repository/UserRepository.java create mode 100644 springboot-starter-data-authorization/src/test/resources/application.properties diff --git a/README.md b/README.md index 68fea27b..74f5fe9a 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ v.3.x 为springboot 3.x版本,使用jdk17版本 * springboot-starter | Springboot领域驱动框架 * springboot-starter-data-fast | 快速数据呈现框架 +* springboot-starter-data-authorization | 数据权限框架 * springboot-starter-flow | 流程引擎框架 * springboot-starter-security | security权限框架支持基于JWT的无状态权限认证与Redis的有状态权限认证 @@ -42,6 +43,13 @@ v.3.x 为springboot 3.x版本,使用jdk17版本 ${last.version} + + + com.codingapi.springboot + springboot-starter-data-authorization + ${last.version} + + com.codingapi.springboot diff --git a/pom.xml b/pom.xml index 69009d6e..3ac27491 100644 --- a/pom.xml +++ b/pom.xml @@ -3,6 +3,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 pom + + springboot-starter-data-authorization + org.springframework.boot spring-boot-starter-parent @@ -12,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.14 + 2.9.15 https://github.com/codingapi/springboot-framewrok springboot-parent @@ -45,6 +48,7 @@ 4.0.24 2.3.232 5.6.2 + 5.0 @@ -209,6 +213,12 @@ ${apache-groovy.version} + + com.github.jsqlparser + jsqlparser + ${jsqlparser.version} + + @@ -269,9 +279,10 @@ springboot-starter - springboot-starter-flow springboot-starter-security + springboot-starter-data-authorization springboot-starter-data-fast + springboot-starter-flow @@ -281,9 +292,10 @@ springboot-starter - springboot-starter-flow springboot-starter-security + springboot-starter-data-authorization springboot-starter-data-fast + springboot-starter-flow @@ -331,9 +343,10 @@ springboot-starter - springboot-starter-flow springboot-starter-security + springboot-starter-data-authorization springboot-starter-data-fast + springboot-starter-flow diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml new file mode 100644 index 00000000..4be50638 --- /dev/null +++ b/springboot-starter-data-authorization/pom.xml @@ -0,0 +1,77 @@ + + + 4.0.0 + + springboot-parent + com.codingapi.springboot + 2.9.15 + + + springboot-starter-data-authorization + springboot-starter-data-authorization project for Spring Boot + + + 8 + + + + + + com.github.jsqlparser + jsqlparser + + + + org.springframework.boot + spring-boot-starter-data-jpa + test + + + + org.springframework.boot + spring-boot-starter-data-jdbc + test + + + + com.h2database + h2 + test + + + + com.mysql + mysql-connector-j + test + + + + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.12 + + + + prepare-agent + + + + report + test + + report + + + + + + + + diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/DataAuthorizationConfiguration.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/DataAuthorizationConfiguration.java new file mode 100644 index 00000000..e36e604a --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/DataAuthorizationConfiguration.java @@ -0,0 +1,48 @@ +package com.codingapi.springboot.authorization; + + +import com.codingapi.springboot.authorization.filter.DataAuthorizationFilter; +import com.codingapi.springboot.authorization.handler.ColumnHandler; +import com.codingapi.springboot.authorization.handler.RowHandler; +import com.codingapi.springboot.authorization.interceptor.SQLInterceptor; +import com.codingapi.springboot.authorization.properties.DataAuthorizationProperties; +import com.codingapi.springboot.authorization.register.ConditionHandlerRegister; +import com.codingapi.springboot.authorization.register.DataAuthorizationContextRegister; +import com.codingapi.springboot.authorization.register.ResultSetHandlerRegister; +import com.codingapi.springboot.authorization.register.SQLInterceptorRegister; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +@Configuration +public class DataAuthorizationConfiguration { + + @Bean + @ConfigurationProperties(prefix = "codingapi.data-authorization") + public DataAuthorizationProperties dataAuthorizationProperties(){ + return new DataAuthorizationProperties(); + } + + @Bean + public ConditionHandlerRegister conditionHandlerRegister(@Autowired(required = false) RowHandler rowHandler) { + return new ConditionHandlerRegister(rowHandler); + } + + @Bean + public ResultSetHandlerRegister resultSetHandlerRegister(@Autowired(required = false) ColumnHandler columnHandler) { + return new ResultSetHandlerRegister(columnHandler); + } + + @Bean + public SQLInterceptorRegister sqlInterceptorRegister(@Autowired(required = false) SQLInterceptor sqlInterceptor) { + return new SQLInterceptorRegister(sqlInterceptor); + } + + @Bean + public DataAuthorizationContextRegister dataAuthorizationContextRegister(@Autowired(required = false) List dataAuthorizationFilters) { + return new DataAuthorizationContextRegister(dataAuthorizationFilters); + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/DataAuthorizationContext.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/DataAuthorizationContext.java new file mode 100644 index 00000000..9af58d8a --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/DataAuthorizationContext.java @@ -0,0 +1,81 @@ +package com.codingapi.springboot.authorization; + +import com.codingapi.springboot.authorization.filter.DataAuthorizationFilter; +import com.codingapi.springboot.authorization.handler.Condition; +import com.codingapi.springboot.authorization.interceptor.SQLInterceptState; +import lombok.Getter; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * 数据权限上下文 + */ +public class DataAuthorizationContext { + + @Getter + private final static DataAuthorizationContext instance = new DataAuthorizationContext(); + + private final List filters; + + private DataAuthorizationContext() { + this.filters = new ArrayList<>(); + } + + /** + * 添加数据权限过滤器 + * @param filter 数据权限过滤器 + */ + public void addDataAuthorizationFilter(DataAuthorizationFilter filter) { + this.filters.add(filter); + } + + /** + * 清空数据权限过滤器 + */ + public void clearDataAuthorizationFilters() { + this.filters.clear(); + } + + /** + * 列权限 + * @param interceptState 拦截状态 + * @param tableName 表名(或别名) + * @param columnName 列名 (或别名) + * @param value 值 + * @return T + * @param 泛型 + */ + public T columnAuthorization(SQLInterceptState interceptState, String tableName, String columnName, T value) { + if (interceptState != null && interceptState.hasIntercept()) { + String realTableName = interceptState.getTableName(tableName); + String realColumnName = interceptState.getColumnName(tableName,columnName); + + for (DataAuthorizationFilter filter : filters) { + if (filter.supportColumnAuthorization(realTableName, realColumnName, value)) { + return filter.columnAuthorization(realTableName, realColumnName, value); + } + } + } + return value; + } + + /** + * 行权限 + * @param tableName 表名 + * @param tableAlias 别名 + * @return Condition 增加的过滤条件 + */ + public Condition rowAuthorization(String tableName, String tableAlias) { + if (StringUtils.hasText(tableName) && StringUtils.hasText(tableAlias)) { + for (DataAuthorizationFilter filter : filters) { + if (filter.supportRowAuthorization(tableName, tableAlias)) { + return filter.rowAuthorization(tableName, tableAlias); + } + } + } + return null; + } + +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/DataPermissionSQLEnhancer.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/DataPermissionSQLEnhancer.java new file mode 100644 index 00000000..66a91a6b --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/DataPermissionSQLEnhancer.java @@ -0,0 +1,110 @@ +package com.codingapi.springboot.authorization.enhancer; + + +import com.codingapi.springboot.authorization.handler.Condition; +import com.codingapi.springboot.authorization.handler.RowHandler; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.select.FromItem; +import net.sf.jsqlparser.statement.select.Join; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; + +import java.sql.SQLException; + +/** + * 数据权限 SQL 增强器 + */ +public class DataPermissionSQLEnhancer { + + private final String sql; + private final RowHandler rowHandler; + private final TableColumnAliasHolder tableColumnAliasHolder; + private final Statement statement; + + + // 构造函数 + public DataPermissionSQLEnhancer(String sql, RowHandler rowHandler) throws SQLException { + try { + // 如何sql中存在? 则在?后面添加空格 + this.sql = sql.replaceAll("\\?", " ? "); + this.rowHandler = rowHandler; + this.statement = CCJSqlParserUtil.parse(this.sql); + this.tableColumnAliasHolder = new TableColumnAliasHolder(statement); + } catch (Exception e) { + throw new SQLException(e); + } + } + + // 获取增强后的SQL + public String getNewSQL() throws SQLException { + try { + if (statement instanceof Select) { + tableColumnAliasHolder.holderAlias(); + Select select = (Select) statement; + PlainSelect plainSelect = select.getPlainSelect(); + this.enhanceDataPermissionInSelect(plainSelect); + return statement.toString(); + } + } catch (Exception e) { + throw new SQLException(e); + } + return sql; + } + + public TableColumnAliasContext getTableAlias() { + return tableColumnAliasHolder.getAliasContext(); + } + + + // 增强 SELECT 语句 + private void enhanceDataPermissionInSelect(PlainSelect plainSelect) throws Exception { + FromItem fromItem = plainSelect.getFromItem(); + + // FROM 项是表 + if (fromItem instanceof Table) { + Table table = (Table) fromItem; + this.injectDataPermissionCondition(plainSelect, table, plainSelect.getWhere()); + } + + // FROM是子查询 + if (fromItem instanceof Select) { + PlainSelect subPlainSelect = ((Select) fromItem).getPlainSelect(); + this.enhanceDataPermissionInSelect(subPlainSelect); + } + + // 处理JOIN或关联子查询 + if (plainSelect.getJoins() != null) { + for (Join join : plainSelect.getJoins()) { + if (join.getRightItem() instanceof Select) { + PlainSelect subPlainSelect = ((Select) join.getRightItem()).getPlainSelect(); + this.enhanceDataPermissionInSelect(subPlainSelect); + } + if (join.getRightItem() instanceof Table) { + injectDataPermissionCondition(plainSelect, (Table) join.getRightItem(), plainSelect.getWhere()); + } + } + } + } + + + // 注入数据权限条件 + private void injectDataPermissionCondition(PlainSelect plainSelect, Table table, Expression where) throws Exception { + String tableName = table.getName(); + String aliaName = table.getAlias() != null ? table.getAlias().getName() : tableName; + Condition condition = rowHandler.handler(plainSelect.toString(), tableName, aliaName); + if (condition != null) { + // 添加自定义条件 + Expression customExpression = CCJSqlParserUtil.parseCondExpression(condition.getCondition()); + if (where != null) { + plainSelect.setWhere(new AndExpression(customExpression, where)); + } else { + plainSelect.setWhere(customExpression); + } + } + } + +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/TableColumnAlias.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/TableColumnAlias.java new file mode 100644 index 00000000..bd12a457 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/TableColumnAlias.java @@ -0,0 +1,62 @@ +package com.codingapi.springboot.authorization.enhancer; + +import lombok.Getter; + +import java.util.Map; + +/** + * 表字段别名 + */ +public class TableColumnAlias { + + private final String parent; + @Getter + private final String tableName; + @Getter + private final String columnName; + private final String aliasName; + + public TableColumnAlias(String parent, String tableName, String columnName, String aliasName) { + this.parent = parent; + this.tableName = tableName; + this.columnName = columnName; + if (aliasName != null) { + this.aliasName = aliasName + .replaceAll("`", "") + .replaceAll("\"", "") + .replaceAll("'", "") + .trim(); + } else { + this.aliasName = null; + } + } + + /** + * 是否是表 + * + * @param tableAlias 表别名 + * @return 是否是表 + */ + public boolean isTable(Map tableAlias) { + return tableAlias.containsKey(tableName); + } + + + /** + * 获取父级key + * + * @return key + */ + public String getParentKey() { + return parent + "." + aliasName; + } + + /** + * 获取表别名key + * + * @return key + */ + public String getTableAliasKey() { + return tableName + "." + aliasName; + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/TableColumnAliasContext.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/TableColumnAliasContext.java new file mode 100644 index 00000000..f557e2ef --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/TableColumnAliasContext.java @@ -0,0 +1,92 @@ +package com.codingapi.springboot.authorization.enhancer; + +import lombok.Getter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 表、字段别名上下文 + */ +public class TableColumnAliasContext { + + private final List columnAliases; + @Getter + private final Map tableAlias; + + private final Map columnAliasMap; + + protected TableColumnAliasContext() { + this.columnAliases = new ArrayList<>(); + this.tableAlias = new HashMap<>(); + this.columnAliasMap = new HashMap<>(); + } + + /** + * 添加表别名 + * @param tableAlias 表别名 + * @param tableName 表名 + */ + protected void addTable(String tableAlias, String tableName) { + this.tableAlias.put(tableAlias, tableName); + } + + /** + * 添加字段别名 + * @param parent 父级(上级别名) + * @param tableName 表名 + * @param columnName 字段名 + * @param aliasName 别名 + */ + protected void addColumn(String parent, String tableName, String columnName, String aliasName) { + TableColumnAlias tableColumnAlias = new TableColumnAlias(parent, tableName, columnName, aliasName); + columnAliases.add(tableColumnAlias); + } + + /** + * 列别名转换为map + */ + protected void columnKeyToMap() { + for (TableColumnAlias tableColumnAlias : columnAliases) { + if (tableColumnAlias.isTable(tableAlias)) { + String parentKey = tableColumnAlias.getParentKey(); + String tableAliasName = tableColumnAlias.getTableName(); + String tableName = this.getTableName(tableAliasName); + String columnValue = tableName + "." + tableColumnAlias.getColumnName(); + columnAliasMap.put(parentKey, columnValue); + columnAliasMap.put(tableColumnAlias.getTableAliasKey(), columnValue); + } + } + } + + /** + * 获取表名(真实表名) + * @param tableName 表名或表别名 + * @return 真实表名 + */ + public String getTableName(String tableName) { + String value = tableAlias.get(tableName); + if (value != null) { + return value; + } + return tableName; + } + + + /** + * 获取字段名(真实字段名) + * @param tableName 表名或表别名 + * @param columnName 字段名 + * @return 真实字段名 + */ + public String getColumnName(String tableName, String columnName) { + String key = tableName + "." + columnName; + String value = columnAliasMap.get(key); + if (value != null) { + return value.split("\\.")[1]; + } + return columnName; + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/TableColumnAliasHolder.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/TableColumnAliasHolder.java new file mode 100644 index 00000000..340c3a4e --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/TableColumnAliasHolder.java @@ -0,0 +1,115 @@ +package com.codingapi.springboot.authorization.enhancer; + +import lombok.Getter; +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.select.*; + +import java.util.List; + +/** + * 表列别名持有者 + */ +public class TableColumnAliasHolder { + + private final Statement statement; + @Getter + private final TableColumnAliasContext aliasContext; + + public TableColumnAliasHolder(Statement statement) { + this.statement = statement; + this.aliasContext = new TableColumnAliasContext(); + } + + + /** + * 获取表列别名 + */ + public void holderAlias() { + Select select = (Select) statement; + PlainSelect plainSelect = select.getPlainSelect(); + this.searchSubSelect(null, plainSelect); + aliasContext.columnKeyToMap(); + } + + + // 增强 SELECT 语句 + private void searchSubSelect(String parent, PlainSelect plainSelect) { + FromItem fromItem = plainSelect.getFromItem(); + + // FROM 项是表 + if (fromItem instanceof Table) { + this.appendTableAlias(fromItem); + Table table = (Table) fromItem; + this.appendColumnAlias(parent, table.getName(), plainSelect.getSelectItems()); + } + + + // FROM是子查询 + if (fromItem instanceof Select) { + PlainSelect subPlainSelect = ((Select) fromItem).getPlainSelect(); + this.appendColumnAlias(parent, null, plainSelect.getSelectItems()); + parent = fromItem.getAlias().getName(); + this.searchSubSelect(parent, subPlainSelect); + } + + // 处理JOIN或关联子查询 + if (plainSelect.getJoins() != null) { + for (Join join : plainSelect.getJoins()) { + if (join.getRightItem() instanceof Select) { + FromItem currentItem = join.getRightItem(); + PlainSelect subPlainSelect = ((Select) currentItem).getPlainSelect(); + this.appendColumnAlias(parent, null, plainSelect.getSelectItems()); + parent = currentItem.getAlias().getName(); + this.searchSubSelect(parent, subPlainSelect); + } + if (join.getRightItem() instanceof Table) { + FromItem currentItem = join.getRightItem(); + this.appendTableAlias(currentItem); + Table table = (Table) currentItem; + this.appendColumnAlias(parent, table.getName(), plainSelect.getSelectItems()); + } + } + } + } + + + /** + * 添加表别名 + * + * @param fromItem 表 + */ + private void appendTableAlias(FromItem fromItem) { + Table table = (Table) fromItem; + Alias alias = table.getAlias(); + String aliasName = alias != null ? alias.getName() : table.getName(); + aliasContext.addTable(aliasName, table.getName()); + } + + + /** + * 添加列别名 + * + * @param parent 父表别名 + * @param selectItems 列 + */ + private void appendColumnAlias(String parent, String tableName, List> selectItems) { + if (selectItems != null) { + for (SelectItem selectItem : selectItems) { + if (selectItem.getExpression() instanceof Column) { + Column column = (Column) selectItem.getExpression(); + if (column.getTable() != null) { + tableName = column.getTable().getName(); + } + String columnName = column.getColumnName(); + Alias columnAlias = selectItem.getAlias(); + String aliasName = columnAlias != null ? selectItem.getAlias().getName() : columnName; + aliasContext.addColumn(parent, tableName, columnName, aliasName); + } + } + } + } + +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/exception/NotAuthorizationException.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/exception/NotAuthorizationException.java new file mode 100644 index 00000000..29a7f385 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/exception/NotAuthorizationException.java @@ -0,0 +1,13 @@ +package com.codingapi.springboot.authorization.exception; + +import java.sql.SQLException; + +public class NotAuthorizationException extends SQLException { + + public NotAuthorizationException() { + } + + public NotAuthorizationException(String reason) { + super(reason); + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/filter/DataAuthorizationFilter.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/filter/DataAuthorizationFilter.java new file mode 100644 index 00000000..69789928 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/filter/DataAuthorizationFilter.java @@ -0,0 +1,44 @@ +package com.codingapi.springboot.authorization.filter; + +import com.codingapi.springboot.authorization.handler.Condition; + +/** + * 数据权限过滤器 + */ +public interface DataAuthorizationFilter { + + /** + * 列权限过滤 + * @param tableName 表名 + * @param columnName 列名 + * @param value 值 + * @return 过滤后的值 + * @param T + */ + T columnAuthorization(String tableName, String columnName,T value); + + /** + * 行权限过滤 + * @param tableName 表名 + * @param tableAlias 表别名 + * @return 过滤后拦截sql条件 + */ + Condition rowAuthorization(String tableName, String tableAlias); + + /** + * 是否支持列权限过滤 + * @param tableName 表名 + * @param columnName 列名 + * @param value 值 + * @return 是否支持 + */ + boolean supportColumnAuthorization(String tableName, String columnName, Object value); + + /** + * 是否支持行权限过滤 + * @param tableName 表名 + * @param tableAlias 表别名 + * @return 是否支持 + */ + boolean supportRowAuthorization(String tableName, String tableAlias); +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/filter/DefaultDataAuthorizationFilter.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/filter/DefaultDataAuthorizationFilter.java new file mode 100644 index 00000000..ecc26881 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/filter/DefaultDataAuthorizationFilter.java @@ -0,0 +1,27 @@ +package com.codingapi.springboot.authorization.filter; + +import com.codingapi.springboot.authorization.handler.Condition; + +public class DefaultDataAuthorizationFilter implements DataAuthorizationFilter{ + + @Override + public T columnAuthorization(String tableName, String columnName, T value) { + return value; + } + + @Override + public Condition rowAuthorization(String tableName, String tableAlias) { + return null; + } + + @Override + public boolean supportColumnAuthorization(String tableName, String columnName, Object value) { + return false; + } + + @Override + public boolean supportRowAuthorization(String tableName, String tableAlias) { + return false; + } + +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/ColumnHandler.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/ColumnHandler.java new file mode 100644 index 00000000..0f1da82a --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/ColumnHandler.java @@ -0,0 +1,74 @@ +package com.codingapi.springboot.authorization.handler; + +import com.codingapi.springboot.authorization.interceptor.SQLInterceptState; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.*; + +/** + * 列表拦截器 + */ +public interface ColumnHandler { + + String getString(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, String value); + + boolean getBoolean(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, boolean value); + + byte getByte(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, byte value); + + short getShort(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, short value); + + int getInt(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, int value); + + long getLong(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, long value); + + float getFloat(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, float value); + + double getDouble(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, double value); + + BigDecimal getBigDecimal(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, BigDecimal value); + + byte[] getBytes(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, byte[] value); + + Date getDate(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Date value); + + Time getTime(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Time value); + + Timestamp getTimestamp(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Timestamp value); + + InputStream getAsciiStream(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, InputStream value); + + InputStream getUnicodeStream(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, InputStream value); + + InputStream getBinaryStream(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, InputStream value); + + Object getObject(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Object value); + + Reader getCharacterStream(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Reader value); + + Ref getRef(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Ref value); + + Blob getBlob(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Blob value); + + Clob getClob(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Clob value); + + Array getArray(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Array value); + + URL getURL(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, URL value); + + NClob getNClob(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, NClob value); + + SQLXML getSQLXML(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, SQLXML value); + + String getNString(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, String value); + + Reader getNCharacterStream(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Reader value); + + RowId getRowId(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, RowId value); + + T getObject(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, T value, Class type); + +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/ColumnHandlerContext.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/ColumnHandlerContext.java new file mode 100644 index 00000000..fc0ef6d3 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/ColumnHandlerContext.java @@ -0,0 +1,144 @@ +package com.codingapi.springboot.authorization.handler; + +import com.codingapi.springboot.authorization.interceptor.SQLInterceptState; +import lombok.Getter; +import lombok.Setter; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.*; + +/** + * ResultSet 拦截器 + */ +@Setter +public class ColumnHandlerContext { + + @Getter + private final static ColumnHandlerContext instance = new ColumnHandlerContext(); + + private ColumnHandlerContext() { + this.columnHandler = new DefaultColumnHandler(); + } + + private ColumnHandler columnHandler; + + + public String getString(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, String value) { + return columnHandler.getString(interceptState, columnIndex, tableName, columnName, value); + } + + public short getShort(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, short value) { + return columnHandler.getShort(interceptState, columnIndex, tableName, columnName, value); + } + + public boolean getBoolean(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, boolean value) { + return columnHandler.getBoolean(interceptState, columnIndex, tableName, columnName, value); + } + + public byte getByte(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, byte value) { + return columnHandler.getByte(interceptState, columnIndex, tableName, columnName, value); + } + + public int getInt(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, int value) { + return columnHandler.getInt(interceptState, columnIndex, tableName, columnName, value); + } + + public long getLong(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, long value) { + return columnHandler.getLong(interceptState, columnIndex, tableName, columnName, value); + } + + public float getFloat(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, float value) { + return columnHandler.getFloat(interceptState, columnIndex, tableName, columnName, value); + } + + public double getDouble(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, double value) { + return columnHandler.getDouble(interceptState, columnIndex, tableName, columnName, value); + } + + public BigDecimal getBigDecimal(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, BigDecimal value) { + return columnHandler.getBigDecimal(interceptState, columnIndex, tableName, columnName, value); + } + + public byte[] getBytes(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, byte[] value) { + return columnHandler.getBytes(interceptState, columnIndex, tableName, columnName, value); + } + + public Timestamp getTimestamp(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Timestamp value) { + return columnHandler.getTimestamp(interceptState, columnIndex, tableName, columnName, value); + } + + public Time getTime(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Time value) { + return columnHandler.getTime(interceptState, columnIndex, tableName, columnName, value); + } + + public Date getDate(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Date value) { + return columnHandler.getDate(interceptState, columnIndex, tableName, columnName, value); + } + + public InputStream getAsciiStream(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, InputStream value) { + return columnHandler.getAsciiStream(interceptState, columnIndex, tableName, columnName, value); + } + + public InputStream getUnicodeStream(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, InputStream value) { + return columnHandler.getUnicodeStream(interceptState, columnIndex, tableName, columnName, value); + } + + public InputStream getBinaryStream(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, InputStream value) { + return columnHandler.getBinaryStream(interceptState, columnIndex, tableName, columnName, value); + } + + public Object getObject(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Object value) { + return columnHandler.getObject(interceptState, columnIndex, tableName, columnName, value); + } + + public Reader getCharacterStream(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Reader value) { + return columnHandler.getCharacterStream(interceptState, columnIndex, tableName, columnName, value); + } + + public Ref getRef(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Ref value) { + return columnHandler.getRef(interceptState, columnIndex, tableName, columnName, value); + } + + public Blob getBlob(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Blob value) { + return columnHandler.getBlob(interceptState, columnIndex, tableName, columnName, value); + } + + public Clob getClob(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Clob value) { + return columnHandler.getClob(interceptState, columnIndex, tableName, columnName, value); + } + + public Array getArray(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Array value) { + return columnHandler.getArray(interceptState, columnIndex, tableName, columnName, value); + } + + public URL getURL(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, URL value) { + return columnHandler.getURL(interceptState, columnIndex, tableName, columnName, value); + } + + public NClob getNClob(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, NClob value) { + return columnHandler.getNClob(interceptState, columnIndex, tableName, columnName, value); + } + + public SQLXML getSQLXML(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, SQLXML value) { + return columnHandler.getSQLXML(interceptState, columnIndex, tableName, columnName, value); + } + + public String getNString(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, String value) { + return columnHandler.getNString(interceptState, columnIndex, tableName, columnName, value); + } + + public Reader getNCharacterStream(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Reader value) { + return columnHandler.getNCharacterStream(interceptState, columnIndex, tableName, columnName, value); + } + + public RowId getRowId(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, RowId value) { + return columnHandler.getRowId(interceptState, columnIndex, tableName, columnName, value); + } + + public T getObject(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, T value, Class type) { + return columnHandler.getObject(interceptState, columnIndex, tableName, columnName, value, type); + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/Condition.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/Condition.java new file mode 100644 index 00000000..3cb6d62e --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/Condition.java @@ -0,0 +1,33 @@ +package com.codingapi.springboot.authorization.handler; + +import lombok.Getter; + +/** + * 查询条件 + */ +@Getter +public class Condition { + + private final String condition; + + private Condition(String condition) { + this.condition = condition; + } + + public static Condition customCondition(String condition) { + return new Condition(condition); + } + + public static Condition formatCondition(String condition, Object... args) { + return new Condition(String.format(condition, args)); + } + + public static Condition emptyCondition() { + return null; + } + + public static Condition defaultCondition() { + return new Condition("1=1"); + } + +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/DefaultColumnHandler.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/DefaultColumnHandler.java new file mode 100644 index 00000000..ff60b751 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/DefaultColumnHandler.java @@ -0,0 +1,161 @@ +package com.codingapi.springboot.authorization.handler; + +import com.codingapi.springboot.authorization.DataAuthorizationContext; +import com.codingapi.springboot.authorization.interceptor.SQLInterceptState; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.*; + +/** + * 默认ResultSet 拦截器 + */ +public class DefaultColumnHandler implements ColumnHandler { + + @Override + public String getString(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, String value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public boolean getBoolean(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, boolean value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public byte getByte(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, byte value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public short getShort(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, short value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public int getInt(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, int value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public long getLong(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, long value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public float getFloat(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, float value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public double getDouble(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, double value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public BigDecimal getBigDecimal(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, BigDecimal value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public byte[] getBytes(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, byte[] value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public Date getDate(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Date value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public Time getTime(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Time value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public Timestamp getTimestamp(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Timestamp value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public InputStream getAsciiStream(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, InputStream value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public InputStream getUnicodeStream(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, InputStream value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public InputStream getBinaryStream(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, InputStream value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public Object getObject(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Object value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public Reader getCharacterStream(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Reader value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public Ref getRef(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Ref value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public Blob getBlob(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Blob value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public Clob getClob(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Clob value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public Array getArray(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Array value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public URL getURL(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, URL value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public NClob getNClob(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, NClob value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public SQLXML getSQLXML(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, SQLXML value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public String getNString(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, String value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public Reader getNCharacterStream(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, Reader value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public RowId getRowId(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, RowId value) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } + + @Override + public T getObject(SQLInterceptState interceptState, int columnIndex, String tableName, String columnName, T value, Class type) { + return DataAuthorizationContext.getInstance().columnAuthorization(interceptState,tableName, columnName, value); + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/DefaultRowHandler.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/DefaultRowHandler.java new file mode 100644 index 00000000..972075cf --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/DefaultRowHandler.java @@ -0,0 +1,11 @@ +package com.codingapi.springboot.authorization.handler; + +import com.codingapi.springboot.authorization.DataAuthorizationContext; + +public class DefaultRowHandler implements RowHandler { + + @Override + public Condition handler(String subSql, String tableName, String tableAlias) { + return DataAuthorizationContext.getInstance().rowAuthorization(tableName, tableAlias); + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/RowHandler.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/RowHandler.java new file mode 100644 index 00000000..7262d5c4 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/RowHandler.java @@ -0,0 +1,19 @@ +package com.codingapi.springboot.authorization.handler; + +/** + * 行数据权限处理器 + */ +public interface RowHandler { + + /** + * 查询条件拦截 + * + * @param subSql 查询子SQL语句 + * @param tableName 表名 + * @param tableAlias 表别名 + * @return 条件语句 + */ + Condition handler(String subSql, String tableName, String tableAlias); + + +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/RowHandlerContext.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/RowHandlerContext.java new file mode 100644 index 00000000..5d096177 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/handler/RowHandlerContext.java @@ -0,0 +1,18 @@ +package com.codingapi.springboot.authorization.handler; + +import lombok.Getter; +import lombok.Setter; + +@Setter +public class RowHandlerContext { + + @Getter + private final static RowHandlerContext instance = new RowHandlerContext(); + + @Getter + private RowHandler rowHandler; + + private RowHandlerContext() { + this.rowHandler = new DefaultRowHandler(); + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/DataPermissionSQL.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/DataPermissionSQL.java new file mode 100644 index 00000000..b0b47df8 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/DataPermissionSQL.java @@ -0,0 +1,18 @@ +package com.codingapi.springboot.authorization.interceptor; + +import com.codingapi.springboot.authorization.enhancer.TableColumnAliasContext; +import lombok.Getter; + +@Getter +public class DataPermissionSQL { + + private final String sql; + private final String newSql; + private final TableColumnAliasContext aliasContext; + + public DataPermissionSQL(String sql, String newSql, TableColumnAliasContext aliasContext) { + this.sql = sql; + this.newSql = newSql; + this.aliasContext = aliasContext; + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/DefaultSQLInterceptor.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/DefaultSQLInterceptor.java new file mode 100644 index 00000000..8d658633 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/DefaultSQLInterceptor.java @@ -0,0 +1,35 @@ +package com.codingapi.springboot.authorization.interceptor; + + +import com.codingapi.springboot.authorization.enhancer.DataPermissionSQLEnhancer; +import com.codingapi.springboot.authorization.handler.RowHandler; +import com.codingapi.springboot.authorization.handler.RowHandlerContext; +import com.codingapi.springboot.authorization.properties.DataAuthorizationPropertyContext; +import com.codingapi.springboot.authorization.utils.SQLUtils; +import lombok.extern.slf4j.Slf4j; + +import java.sql.SQLException; + +@Slf4j +public class DefaultSQLInterceptor implements SQLInterceptor { + + @Override + public boolean beforeHandler(String sql) { + return SQLUtils.isQuerySql(sql); + } + + + @Override + public void afterHandler(String sql, String newSql, SQLException exception) { + if (DataAuthorizationPropertyContext.getInstance().showSql()) { + log.info("newSql:{}", newSql); + } + } + + @Override + public DataPermissionSQL postHandler(String sql) throws SQLException { + RowHandler rowHandler = RowHandlerContext.getInstance().getRowHandler(); + DataPermissionSQLEnhancer sqlEnhancer = new DataPermissionSQLEnhancer(sql, rowHandler); + return new DataPermissionSQL(sql, sqlEnhancer.getNewSQL(), sqlEnhancer.getTableAlias()); + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLInterceptState.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLInterceptState.java new file mode 100644 index 00000000..809668fb --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLInterceptState.java @@ -0,0 +1,60 @@ +package com.codingapi.springboot.authorization.interceptor; + +import com.codingapi.springboot.authorization.enhancer.TableColumnAliasContext; + +/** + * SQL拦截状态 + */ +public class SQLInterceptState { + + private final boolean state; + + private final String sql; + + private final String newSql; + + private final TableColumnAliasContext aliasContext; + + private SQLInterceptState(boolean state, String sql, String newSql, TableColumnAliasContext aliasContext) { + this.state = state; + this.sql = sql; + this.newSql = newSql; + this.aliasContext = aliasContext; + } + + /** + * 拦截 + */ + public static SQLInterceptState intercept(String sql, String newSql, TableColumnAliasContext aliasContext) { + return new SQLInterceptState(true, sql, newSql, aliasContext); + } + + /** + * 不拦截 + */ + public static SQLInterceptState unIntercept(String sql) { + return new SQLInterceptState(false, sql, sql, null); + } + + public String getTableName(String tableName) { + return aliasContext.getTableName(tableName); + } + + public String getColumnName(String tableName, String columnName) { + return aliasContext.getColumnName(tableName, columnName); + } + + public String getSql() { + if (state) { + return newSql; + } else { + return sql; + } + } + + public boolean hasIntercept() { + return state; + } + + +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLInterceptor.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLInterceptor.java new file mode 100644 index 00000000..dd627f5d --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLInterceptor.java @@ -0,0 +1,39 @@ +package com.codingapi.springboot.authorization.interceptor; + +import java.sql.SQLException; + +/** + * SQL查询条件处理器 + */ +public interface SQLInterceptor { + + + /** + * 前置处理 + * + * @param sql sql + * @return 是否处理 + */ + boolean beforeHandler(String sql); + + + /** + * 处理sql + * + * @param sql sql + * @return 处理后的sql newSql + * @throws SQLException + */ + DataPermissionSQL postHandler(String sql) throws SQLException; + + + /** + * 后置处理 + * @param sql sql + * @param newSql newSql + * @param exception exception + */ + void afterHandler(String sql, String newSql, SQLException exception); + + +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLInterceptorContext.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLInterceptorContext.java new file mode 100644 index 00000000..178de534 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLInterceptorContext.java @@ -0,0 +1,20 @@ +package com.codingapi.springboot.authorization.interceptor; + +import lombok.Getter; +import lombok.Setter; + +@Setter +public class SQLInterceptorContext { + + @Getter + private final static SQLInterceptorContext instance = new SQLInterceptorContext(); + + @Getter + private SQLInterceptor sqlInterceptor; + + private SQLInterceptorContext() { + this.sqlInterceptor = new DefaultSQLInterceptor(); + } + + +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLRunningContext.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLRunningContext.java new file mode 100644 index 00000000..fab68f60 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLRunningContext.java @@ -0,0 +1,67 @@ +package com.codingapi.springboot.authorization.interceptor; + +import lombok.Getter; + +import java.sql.SQLException; + +/** + * SQLRunningContext SQL执行拦截上下文 + */ +public class SQLRunningContext { + + @Getter + private final static SQLRunningContext instance = new SQLRunningContext(); + + private final ThreadLocal skipInterceptor = ThreadLocal.withInitial(() -> false); + + private SQLRunningContext() { + } + + /** + * 拦截SQL + * + * @param sql sql + * @return SQLInterceptState + * @throws SQLException SQLException + */ + public SQLInterceptState intercept(String sql) throws SQLException { + SQLInterceptor sqlInterceptor = SQLInterceptorContext.getInstance().getSqlInterceptor(); + + if (skipInterceptor.get()) { + return SQLInterceptState.unIntercept(sql); + } + try { + if (sqlInterceptor.beforeHandler(sql)) { + // 在拦截器中执行的查询操作将不会被拦截 + skipInterceptor.set(true); + DataPermissionSQL dataPermissionSQL = sqlInterceptor.postHandler(sql); + sqlInterceptor.afterHandler(sql, dataPermissionSQL.getNewSql(), null); + return SQLInterceptState.intercept(sql, dataPermissionSQL.getNewSql(), dataPermissionSQL.getAliasContext()); + } + } catch (SQLException exception) { + sqlInterceptor.afterHandler(sql, null, exception); + } finally { + // 重置拦截器状态 + skipInterceptor.set(false); + } + return SQLInterceptState.unIntercept(sql); + } + + + /** + * 跳过数据权限拦截 + * + * @param supplier 业务逻辑 + * @param T + * @return T + */ + public T skipDataAuthorization(java.util.function.Supplier supplier) { + try { + skipInterceptor.set(true); + return (T) supplier.get(); + } finally { + skipInterceptor.set(false); + } + } + +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/AuthorizationJdbcDriver.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/AuthorizationJdbcDriver.java new file mode 100644 index 00000000..93045795 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/AuthorizationJdbcDriver.java @@ -0,0 +1,57 @@ +package com.codingapi.springboot.authorization.jdbc; + + +import com.codingapi.springboot.authorization.jdbc.proxy.ConnectionProxy; + +import java.sql.*; +import java.util.Enumeration; +import java.util.Properties; +import java.util.logging.Logger; + +public class AuthorizationJdbcDriver implements Driver { + + private Driver driver; + + @Override + public Connection connect(String url, Properties info) throws SQLException { + return new ConnectionProxy(driver.connect(url, info)); + } + + @Override + public boolean acceptsURL(String url) throws SQLException { + Enumeration drivers = DriverManager.getDrivers(); + while (drivers.hasMoreElements()) { + Driver driver = drivers.nextElement(); + if (driver.acceptsURL(url)) { + this.driver = driver; + return true; + } + } + return false; + } + + @Override + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { + return driver.getPropertyInfo(url, info); + } + + @Override + public int getMajorVersion() { + return driver.getMajorVersion(); + } + + @Override + public int getMinorVersion() { + return driver.getMinorVersion(); + } + + @Override + public boolean jdbcCompliant() { + return driver.jdbcCompliant(); + } + + @Override + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + return driver.getParentLogger(); + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/CallableStatementProxy.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/CallableStatementProxy.java new file mode 100644 index 00000000..8f2b77b7 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/CallableStatementProxy.java @@ -0,0 +1,1191 @@ +package com.codingapi.springboot.authorization.jdbc.proxy; + +import com.codingapi.springboot.authorization.interceptor.SQLInterceptState; +import com.codingapi.springboot.authorization.interceptor.SQLRunningContext; +import lombok.AllArgsConstructor; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.*; +import java.util.Calendar; +import java.util.Map; + +@AllArgsConstructor +public class CallableStatementProxy implements CallableStatement { + + private final CallableStatement callableStatement; + + private SQLInterceptState interceptState; + + @Override + public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException { + callableStatement.registerOutParameter(parameterIndex, sqlType); + } + + @Override + public void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException { + callableStatement.registerOutParameter(parameterIndex, sqlType, scale); + } + + @Override + public boolean wasNull() throws SQLException { + return callableStatement.wasNull(); + } + + @Override + public String getString(int parameterIndex) throws SQLException { + return callableStatement.getString(parameterIndex); + } + + @Override + public boolean getBoolean(int parameterIndex) throws SQLException { + return callableStatement.getBoolean(parameterIndex); + } + + @Override + public byte getByte(int parameterIndex) throws SQLException { + return callableStatement.getByte(parameterIndex); + } + + @Override + public short getShort(int parameterIndex) throws SQLException { + return callableStatement.getShort(parameterIndex); + } + + @Override + public int getInt(int parameterIndex) throws SQLException { + return callableStatement.getInt(parameterIndex); + } + + @Override + public long getLong(int parameterIndex) throws SQLException { + return callableStatement.getLong(parameterIndex); + } + + @Override + public float getFloat(int parameterIndex) throws SQLException { + return callableStatement.getFloat(parameterIndex); + } + + @Override + public double getDouble(int parameterIndex) throws SQLException { + return callableStatement.getDouble(parameterIndex); + } + + @Override + public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { + return callableStatement.getBigDecimal(parameterIndex, scale); + } + + @Override + public byte[] getBytes(int parameterIndex) throws SQLException { + return callableStatement.getBytes(parameterIndex); + } + + @Override + public Date getDate(int parameterIndex) throws SQLException { + return callableStatement.getDate(parameterIndex); + } + + @Override + public Time getTime(int parameterIndex) throws SQLException { + return callableStatement.getTime(parameterIndex); + } + + @Override + public Timestamp getTimestamp(int parameterIndex) throws SQLException { + return callableStatement.getTimestamp(parameterIndex); + } + + @Override + public Object getObject(int parameterIndex) throws SQLException { + return callableStatement.getObject(parameterIndex); + } + + @Override + public BigDecimal getBigDecimal(int parameterIndex) throws SQLException { + return callableStatement.getBigDecimal(parameterIndex); + } + + @Override + public Object getObject(int parameterIndex, Map> map) throws SQLException { + return callableStatement.getObject(parameterIndex, map); + } + + @Override + public Ref getRef(int parameterIndex) throws SQLException { + return callableStatement.getRef(parameterIndex); + } + + @Override + public Blob getBlob(int parameterIndex) throws SQLException { + return callableStatement.getBlob(parameterIndex); + } + + @Override + public Clob getClob(int parameterIndex) throws SQLException { + return callableStatement.getClob(parameterIndex); + } + + @Override + public Array getArray(int parameterIndex) throws SQLException { + return callableStatement.getArray(parameterIndex); + } + + @Override + public Date getDate(int parameterIndex, Calendar cal) throws SQLException { + return callableStatement.getDate(parameterIndex, cal); + } + + @Override + public Time getTime(int parameterIndex, Calendar cal) throws SQLException { + return callableStatement.getTime(parameterIndex, cal); + } + + @Override + public Timestamp getTimestamp(int parameterIndex, Calendar cal) throws SQLException { + return callableStatement.getTimestamp(parameterIndex, cal); + } + + @Override + public void registerOutParameter(int parameterIndex, int sqlType, String typeName) throws SQLException { + callableStatement.registerOutParameter(parameterIndex, sqlType, typeName); + } + + @Override + public void registerOutParameter(String parameterName, int sqlType) throws SQLException { + callableStatement.registerOutParameter(parameterName, sqlType); + } + + @Override + public void registerOutParameter(String parameterName, int sqlType, int scale) throws SQLException { + callableStatement.registerOutParameter(parameterName, sqlType, scale); + } + + @Override + public void registerOutParameter(String parameterName, int sqlType, String typeName) throws SQLException { + callableStatement.registerOutParameter(parameterName, sqlType, typeName); + } + + @Override + public URL getURL(int parameterIndex) throws SQLException { + return callableStatement.getURL(parameterIndex); + } + + @Override + public void setURL(String parameterName, URL val) throws SQLException { + callableStatement.setURL(parameterName, val); + } + + @Override + public void setNull(String parameterName, int sqlType) throws SQLException { + callableStatement.setNull(parameterName, sqlType); + } + + @Override + public void setBoolean(String parameterName, boolean x) throws SQLException { + callableStatement.setBoolean(parameterName, x); + } + + @Override + public void setByte(String parameterName, byte x) throws SQLException { + callableStatement.setByte(parameterName, x); + } + + @Override + public void setShort(String parameterName, short x) throws SQLException { + callableStatement.setShort(parameterName, x); + } + + @Override + public void setInt(String parameterName, int x) throws SQLException { + callableStatement.setInt(parameterName, x); + } + + @Override + public void setLong(String parameterName, long x) throws SQLException { + callableStatement.setLong(parameterName, x); + } + + @Override + public void setFloat(String parameterName, float x) throws SQLException { + callableStatement.setFloat(parameterName, x); + } + + @Override + public void setDouble(String parameterName, double x) throws SQLException { + callableStatement.setDouble(parameterName, x); + } + + @Override + public void setBigDecimal(String parameterName, BigDecimal x) throws SQLException { + callableStatement.setBigDecimal(parameterName, x); + } + + @Override + public void setString(String parameterName, String x) throws SQLException { + callableStatement.setString(parameterName, x); + } + + @Override + public void setBytes(String parameterName, byte[] x) throws SQLException { + callableStatement.setBytes(parameterName, x); + } + + @Override + public void setDate(String parameterName, Date x) throws SQLException { + callableStatement.setDate(parameterName, x); + } + + @Override + public void setTime(String parameterName, Time x) throws SQLException { + callableStatement.setTime(parameterName, x); + } + + @Override + public void setTimestamp(String parameterName, Timestamp x) throws SQLException { + callableStatement.setTimestamp(parameterName, x); + } + + @Override + public void setAsciiStream(String parameterName, InputStream x, int length) throws SQLException { + callableStatement.setAsciiStream(parameterName, x, length); + } + + @Override + public void setBinaryStream(String parameterName, InputStream x, int length) throws SQLException { + callableStatement.setBinaryStream(parameterName, x, length); + } + + @Override + public void setObject(String parameterName, Object x, int targetSqlType, int scale) throws SQLException { + callableStatement.setObject(parameterName, x, targetSqlType, scale); + } + + @Override + public void setObject(String parameterName, Object x, int targetSqlType) throws SQLException { + callableStatement.setObject(parameterName, x, targetSqlType); + } + + @Override + public void setObject(String parameterName, Object x) throws SQLException { + callableStatement.setObject(parameterName, x); + } + + @Override + public void setCharacterStream(String parameterName, Reader reader, int length) throws SQLException { + callableStatement.setCharacterStream(parameterName, reader, length); + } + + @Override + public void setDate(String parameterName, Date x, Calendar cal) throws SQLException { + callableStatement.setDate(parameterName, x, cal); + } + + @Override + public void setTime(String parameterName, Time x, Calendar cal) throws SQLException { + callableStatement.setTime(parameterName, x, cal); + } + + @Override + public void setTimestamp(String parameterName, Timestamp x, Calendar cal) throws SQLException { + callableStatement.setTimestamp(parameterName, x, cal); + } + + @Override + public void setNull(String parameterName, int sqlType, String typeName) throws SQLException { + callableStatement.setNull(parameterName, sqlType, typeName); + } + + @Override + public String getString(String parameterName) throws SQLException { + return callableStatement.getString(parameterName); + } + + @Override + public boolean getBoolean(String parameterName) throws SQLException { + return callableStatement.getBoolean(parameterName); + } + + @Override + public byte getByte(String parameterName) throws SQLException { + return callableStatement.getByte(parameterName); + } + + @Override + public short getShort(String parameterName) throws SQLException { + return callableStatement.getShort(parameterName); + } + + @Override + public int getInt(String parameterName) throws SQLException { + return callableStatement.getInt(parameterName); + } + + @Override + public long getLong(String parameterName) throws SQLException { + return callableStatement.getLong(parameterName); + } + + @Override + public float getFloat(String parameterName) throws SQLException { + return callableStatement.getFloat(parameterName); + } + + @Override + public double getDouble(String parameterName) throws SQLException { + return callableStatement.getDouble(parameterName); + } + + @Override + public byte[] getBytes(String parameterName) throws SQLException { + return callableStatement.getBytes(parameterName); + } + + @Override + public Date getDate(String parameterName) throws SQLException { + return callableStatement.getDate(parameterName); + } + + @Override + public Time getTime(String parameterName) throws SQLException { + return callableStatement.getTime(parameterName); + } + + @Override + public Timestamp getTimestamp(String parameterName) throws SQLException { + return callableStatement.getTimestamp(parameterName); + } + + @Override + public Object getObject(String parameterName) throws SQLException { + return callableStatement.getObject(parameterName); + } + + @Override + public BigDecimal getBigDecimal(String parameterName) throws SQLException { + return callableStatement.getBigDecimal(parameterName); + } + + @Override + public Object getObject(String parameterName, Map> map) throws SQLException { + return callableStatement.getObject(parameterName, map); + } + + @Override + public Ref getRef(String parameterName) throws SQLException { + return callableStatement.getRef(parameterName); + } + + @Override + public Blob getBlob(String parameterName) throws SQLException { + return callableStatement.getBlob(parameterName); + } + + @Override + public Clob getClob(String parameterName) throws SQLException { + return callableStatement.getClob(parameterName); + } + + @Override + public Array getArray(String parameterName) throws SQLException { + return callableStatement.getArray(parameterName); + } + + @Override + public Date getDate(String parameterName, Calendar cal) throws SQLException { + return callableStatement.getDate(parameterName, cal); + } + + @Override + public Time getTime(String parameterName, Calendar cal) throws SQLException { + return callableStatement.getTime(parameterName, cal); + } + + @Override + public Timestamp getTimestamp(String parameterName, Calendar cal) throws SQLException { + return callableStatement.getTimestamp(parameterName, cal); + } + + @Override + public URL getURL(String parameterName) throws SQLException { + return callableStatement.getURL(parameterName); + } + + @Override + public RowId getRowId(int parameterIndex) throws SQLException { + return callableStatement.getRowId(parameterIndex); + } + + @Override + public RowId getRowId(String parameterName) throws SQLException { + return callableStatement.getRowId(parameterName); + } + + @Override + public void setRowId(String parameterName, RowId x) throws SQLException { + callableStatement.setRowId(parameterName, x); + } + + @Override + public void setNString(String parameterName, String value) throws SQLException { + callableStatement.setNString(parameterName, value); + } + + @Override + public void setNCharacterStream(String parameterName, Reader value, long length) throws SQLException { + callableStatement.setNCharacterStream(parameterName, value, length); + } + + @Override + public void setNClob(String parameterName, NClob value) throws SQLException { + callableStatement.setNClob(parameterName, value); + } + + @Override + public void setClob(String parameterName, Reader reader, long length) throws SQLException { + callableStatement.setClob(parameterName, reader, length); + } + + @Override + public void setBlob(String parameterName, InputStream inputStream, long length) throws SQLException { + callableStatement.setBlob(parameterName, inputStream, length); + } + + @Override + public void setNClob(String parameterName, Reader reader, long length) throws SQLException { + callableStatement.setNClob(parameterName, reader, length); + } + + @Override + public NClob getNClob(int parameterIndex) throws SQLException { + return callableStatement.getNClob(parameterIndex); + } + + @Override + public NClob getNClob(String parameterName) throws SQLException { + return callableStatement.getNClob(parameterName); + } + + @Override + public void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLException { + callableStatement.setSQLXML(parameterName, xmlObject); + } + + @Override + public SQLXML getSQLXML(int parameterIndex) throws SQLException { + return callableStatement.getSQLXML(parameterIndex); + } + + @Override + public SQLXML getSQLXML(String parameterName) throws SQLException { + return callableStatement.getSQLXML(parameterName); + } + + @Override + public String getNString(int parameterIndex) throws SQLException { + return callableStatement.getNString(parameterIndex); + } + + @Override + public String getNString(String parameterName) throws SQLException { + return callableStatement.getNString(parameterName); + } + + @Override + public Reader getNCharacterStream(int parameterIndex) throws SQLException { + return callableStatement.getNCharacterStream(parameterIndex); + } + + @Override + public Reader getNCharacterStream(String parameterName) throws SQLException { + return callableStatement.getNCharacterStream(parameterName); + } + + @Override + public Reader getCharacterStream(int parameterIndex) throws SQLException { + return callableStatement.getCharacterStream(parameterIndex); + } + + @Override + public Reader getCharacterStream(String parameterName) throws SQLException { + return callableStatement.getCharacterStream(parameterName); + } + + @Override + public void setBlob(String parameterName, Blob x) throws SQLException { + callableStatement.setBlob(parameterName, x); + } + + @Override + public void setClob(String parameterName, Clob x) throws SQLException { + callableStatement.setClob(parameterName, x); + } + + @Override + public void setAsciiStream(String parameterName, InputStream x, long length) throws SQLException { + callableStatement.setAsciiStream(parameterName, x, length); + } + + @Override + public void setBinaryStream(String parameterName, InputStream x, long length) throws SQLException { + callableStatement.setBinaryStream(parameterName, x, length); + } + + @Override + public void setCharacterStream(String parameterName, Reader reader, long length) throws SQLException { + callableStatement.setCharacterStream(parameterName, reader, length); + } + + @Override + public void setAsciiStream(String parameterName, InputStream x) throws SQLException { + callableStatement.setAsciiStream(parameterName, x); + } + + @Override + public void setBinaryStream(String parameterName, InputStream x) throws SQLException { + callableStatement.setBinaryStream(parameterName, x); + } + + @Override + public void setCharacterStream(String parameterName, Reader reader) throws SQLException { + callableStatement.setCharacterStream(parameterName, reader); + } + + @Override + public void setNCharacterStream(String parameterName, Reader value) throws SQLException { + callableStatement.setNCharacterStream(parameterName, value); + } + + @Override + public void setClob(String parameterName, Reader reader) throws SQLException { + callableStatement.setClob(parameterName, reader); + } + + @Override + public void setBlob(String parameterName, InputStream inputStream) throws SQLException { + callableStatement.setBlob(parameterName, inputStream); + } + + @Override + public void setNClob(String parameterName, Reader reader) throws SQLException { + callableStatement.setNClob(parameterName, reader); + } + + @Override + public T getObject(int parameterIndex, Class type) throws SQLException { + return callableStatement.getObject(parameterIndex, type); + } + + @Override + public T getObject(String parameterName, Class type) throws SQLException { + return callableStatement.getObject(parameterName, type); + } + + @Override + public void setObject(String parameterName, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + callableStatement.setObject(parameterName, x, targetSqlType, scaleOrLength); + } + + @Override + public void setObject(String parameterName, Object x, SQLType targetSqlType) throws SQLException { + callableStatement.setObject(parameterName, x, targetSqlType); + } + + @Override + public void registerOutParameter(int parameterIndex, SQLType sqlType) throws SQLException { + callableStatement.registerOutParameter(parameterIndex, sqlType); + } + + @Override + public void registerOutParameter(int parameterIndex, SQLType sqlType, int scale) throws SQLException { + callableStatement.registerOutParameter(parameterIndex, sqlType, scale); + } + + @Override + public void registerOutParameter(int parameterIndex, SQLType sqlType, String typeName) throws SQLException { + callableStatement.registerOutParameter(parameterIndex, sqlType, typeName); + } + + @Override + public void registerOutParameter(String parameterName, SQLType sqlType) throws SQLException { + callableStatement.registerOutParameter(parameterName, sqlType); + } + + @Override + public void registerOutParameter(String parameterName, SQLType sqlType, int scale) throws SQLException { + callableStatement.registerOutParameter(parameterName, sqlType, scale); + } + + @Override + public void registerOutParameter(String parameterName, SQLType sqlType, String typeName) throws SQLException { + callableStatement.registerOutParameter(parameterName, sqlType, typeName); + } + + @Override + public ResultSet executeQuery() throws SQLException { + return new ResultSetProxy(callableStatement.executeQuery(),this.interceptState); + } + + @Override + public int executeUpdate() throws SQLException { + return callableStatement.executeUpdate(); + } + + @Override + public void setNull(int parameterIndex, int sqlType) throws SQLException { + callableStatement.setNull(parameterIndex, sqlType); + } + + @Override + public void setBoolean(int parameterIndex, boolean x) throws SQLException { + callableStatement.setBoolean(parameterIndex, x); + } + + @Override + public void setByte(int parameterIndex, byte x) throws SQLException { + callableStatement.setByte(parameterIndex, x); + } + + @Override + public void setShort(int parameterIndex, short x) throws SQLException { + callableStatement.setShort(parameterIndex, x); + } + + @Override + public void setInt(int parameterIndex, int x) throws SQLException { + callableStatement.setInt(parameterIndex, x); + } + + @Override + public void setLong(int parameterIndex, long x) throws SQLException { + callableStatement.setLong(parameterIndex, x); + } + + @Override + public void setFloat(int parameterIndex, float x) throws SQLException { + callableStatement.setFloat(parameterIndex, x); + } + + @Override + public void setDouble(int parameterIndex, double x) throws SQLException { + callableStatement.setDouble(parameterIndex, x); + } + + @Override + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { + callableStatement.setBigDecimal(parameterIndex, x); + } + + @Override + public void setString(int parameterIndex, String x) throws SQLException { + callableStatement.setString(parameterIndex, x); + } + + @Override + public void setBytes(int parameterIndex, byte[] x) throws SQLException { + callableStatement.setBytes(parameterIndex, x); + } + + @Override + public void setDate(int parameterIndex, Date x) throws SQLException { + callableStatement.setDate(parameterIndex, x); + } + + @Override + public void setTime(int parameterIndex, Time x) throws SQLException { + callableStatement.setTime(parameterIndex, x); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { + callableStatement.setTimestamp(parameterIndex, x); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + callableStatement.setAsciiStream(parameterIndex, x, length); + } + + @Override + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + callableStatement.setUnicodeStream(parameterIndex, x, length); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + callableStatement.setBinaryStream(parameterIndex, x, length); + } + + @Override + public void clearParameters() throws SQLException { + callableStatement.clearParameters(); + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { + callableStatement.setObject(parameterIndex, x, targetSqlType); + } + + @Override + public void setObject(int parameterIndex, Object x) throws SQLException { + callableStatement.setObject(parameterIndex, x); + } + + @Override + public boolean execute() throws SQLException { + return callableStatement.execute(); + } + + @Override + public void addBatch() throws SQLException { + callableStatement.addBatch(); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { + callableStatement.setCharacterStream(parameterIndex, reader, length); + } + + @Override + public void setRef(int parameterIndex, Ref x) throws SQLException { + callableStatement.setRef(parameterIndex, x); + } + + @Override + public void setBlob(int parameterIndex, Blob x) throws SQLException { + callableStatement.setBlob(parameterIndex, x); + } + + @Override + public void setClob(int parameterIndex, Clob x) throws SQLException { + callableStatement.setClob(parameterIndex, x); + } + + @Override + public void setArray(int parameterIndex, Array x) throws SQLException { + callableStatement.setArray(parameterIndex, x); + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + return callableStatement.getMetaData(); + } + + @Override + public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { + callableStatement.setDate(parameterIndex, x, cal); + } + + @Override + public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { + callableStatement.setTime(parameterIndex, x, cal); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { + callableStatement.setTimestamp(parameterIndex, x, cal); + } + + @Override + public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { + callableStatement.setNull(parameterIndex, sqlType, typeName); + } + + @Override + public void setURL(int parameterIndex, URL x) throws SQLException { + callableStatement.setURL(parameterIndex, x); + } + + @Override + public ParameterMetaData getParameterMetaData() throws SQLException { + return callableStatement.getParameterMetaData(); + } + + @Override + public void setRowId(int parameterIndex, RowId x) throws SQLException { + callableStatement.setRowId(parameterIndex, x); + } + + @Override + public void setNString(int parameterIndex, String value) throws SQLException { + callableStatement.setNString(parameterIndex, value); + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { + callableStatement.setNCharacterStream(parameterIndex, value, length); + } + + @Override + public void setNClob(int parameterIndex, NClob value) throws SQLException { + callableStatement.setNClob(parameterIndex, value); + } + + @Override + public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { + callableStatement.setClob(parameterIndex, reader, length); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { + callableStatement.setBlob(parameterIndex, inputStream, length); + } + + @Override + public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + callableStatement.setNClob(parameterIndex, reader, length); + } + + @Override + public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + callableStatement.setSQLXML(parameterIndex, xmlObject); + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException { + callableStatement.setObject(parameterIndex, x, targetSqlType, scaleOrLength); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { + callableStatement.setAsciiStream(parameterIndex, x, length); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { + callableStatement.setBinaryStream(parameterIndex, x, length); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + callableStatement.setCharacterStream(parameterIndex, reader, length); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { + callableStatement.setAsciiStream(parameterIndex, x); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { + callableStatement.setBinaryStream(parameterIndex, x); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { + callableStatement.setCharacterStream(parameterIndex, reader); + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { + callableStatement.setNCharacterStream(parameterIndex, value); + } + + @Override + public void setClob(int parameterIndex, Reader reader) throws SQLException { + callableStatement.setClob(parameterIndex, reader); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { + callableStatement.setBlob(parameterIndex, inputStream); + } + + @Override + public void setNClob(int parameterIndex, Reader reader) throws SQLException { + callableStatement.setNClob(parameterIndex, reader); + } + + @Override + public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + callableStatement.setObject(parameterIndex, x, targetSqlType, scaleOrLength); + } + + @Override + public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException { + callableStatement.setObject(parameterIndex, x, targetSqlType); + } + + @Override + public long executeLargeUpdate() throws SQLException { + return callableStatement.executeLargeUpdate(); + } + + @Override + public ResultSet executeQuery(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return new ResultSetProxy(callableStatement.executeQuery(interceptState.getSql()),this.interceptState); + } + + @Override + public int executeUpdate(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return callableStatement.executeUpdate(interceptState.getSql()); + } + + @Override + public void close() throws SQLException { + callableStatement.close(); + } + + @Override + public int getMaxFieldSize() throws SQLException { + return callableStatement.getMaxFieldSize(); + } + + @Override + public void setMaxFieldSize(int max) throws SQLException { + callableStatement.setMaxFieldSize(max); + } + + @Override + public int getMaxRows() throws SQLException { + return callableStatement.getMaxRows(); + } + + @Override + public void setMaxRows(int max) throws SQLException { + callableStatement.setMaxRows(max); + } + + @Override + public void setEscapeProcessing(boolean enable) throws SQLException { + callableStatement.setEscapeProcessing(enable); + } + + @Override + public int getQueryTimeout() throws SQLException { + return callableStatement.getQueryTimeout(); + } + + @Override + public void setQueryTimeout(int seconds) throws SQLException { + callableStatement.setQueryTimeout(seconds); + } + + @Override + public void cancel() throws SQLException { + callableStatement.cancel(); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return callableStatement.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + callableStatement.clearWarnings(); + } + + @Override + public void setCursorName(String name) throws SQLException { + callableStatement.setCursorName(name); + } + + @Override + public boolean execute(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return callableStatement.execute(interceptState.getSql()); + } + + @Override + public ResultSet getResultSet() throws SQLException { + return new ResultSetProxy(callableStatement.getResultSet(),this.interceptState); + } + + @Override + public int getUpdateCount() throws SQLException { + return callableStatement.getUpdateCount(); + } + + @Override + public boolean getMoreResults() throws SQLException { + return callableStatement.getMoreResults(); + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + callableStatement.setFetchDirection(direction); + } + + @Override + public int getFetchDirection() throws SQLException { + return callableStatement.getFetchDirection(); + } + + @Override + public void setFetchSize(int rows) throws SQLException { + callableStatement.setFetchSize(rows); + } + + @Override + public int getFetchSize() throws SQLException { + return callableStatement.getFetchSize(); + } + + @Override + public int getResultSetConcurrency() throws SQLException { + return callableStatement.getResultSetConcurrency(); + } + + @Override + public int getResultSetType() throws SQLException { + return callableStatement.getResultSetType(); + } + + @Override + public void addBatch(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + callableStatement.addBatch(interceptState.getSql()); + } + + @Override + public void clearBatch() throws SQLException { + callableStatement.clearBatch(); + } + + @Override + public int[] executeBatch() throws SQLException { + return callableStatement.executeBatch(); + } + + @Override + public Connection getConnection() throws SQLException { + return new ConnectionProxy(callableStatement.getConnection()); + } + + @Override + public boolean getMoreResults(int current) throws SQLException { + return callableStatement.getMoreResults(current); + } + + @Override + public ResultSet getGeneratedKeys() throws SQLException { + return callableStatement.getGeneratedKeys(); + } + + @Override + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return callableStatement.executeUpdate(interceptState.getSql(), autoGeneratedKeys); + } + + @Override + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return callableStatement.executeUpdate(interceptState.getSql(), columnIndexes); + } + + @Override + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return callableStatement.executeUpdate(interceptState.getSql(), columnNames); + } + + @Override + public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return callableStatement.execute(interceptState.getSql(), autoGeneratedKeys); + } + + @Override + public boolean execute(String sql, int[] columnIndexes) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return callableStatement.execute(interceptState.getSql(), columnIndexes); + } + + @Override + public boolean execute(String sql, String[] columnNames) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return callableStatement.execute(interceptState.getSql(), columnNames); + } + + @Override + public int getResultSetHoldability() throws SQLException { + return callableStatement.getResultSetHoldability(); + } + + @Override + public boolean isClosed() throws SQLException { + return callableStatement.isClosed(); + } + + @Override + public void setPoolable(boolean poolable) throws SQLException { + callableStatement.setPoolable(poolable); + } + + @Override + public boolean isPoolable() throws SQLException { + return callableStatement.isPoolable(); + } + + @Override + public void closeOnCompletion() throws SQLException { + callableStatement.closeOnCompletion(); + } + + @Override + public boolean isCloseOnCompletion() throws SQLException { + return callableStatement.isCloseOnCompletion(); + } + + @Override + public long getLargeUpdateCount() throws SQLException { + return callableStatement.getLargeUpdateCount(); + } + + @Override + public void setLargeMaxRows(long max) throws SQLException { + callableStatement.setLargeMaxRows(max); + } + + @Override + public long getLargeMaxRows() throws SQLException { + return callableStatement.getLargeMaxRows(); + } + + @Override + public long[] executeLargeBatch() throws SQLException { + return callableStatement.executeLargeBatch(); + } + + @Override + public long executeLargeUpdate(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return callableStatement.executeLargeUpdate(interceptState.getSql()); + } + + @Override + public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return callableStatement.executeLargeUpdate(interceptState.getSql(), autoGeneratedKeys); + } + + @Override + public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return callableStatement.executeLargeUpdate(interceptState.getSql(), columnIndexes); + } + + @Override + public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return callableStatement.executeLargeUpdate(interceptState.getSql(), columnNames); + } + + + @Override + public T unwrap(Class iface) throws SQLException { + return callableStatement.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return callableStatement.isWrapperFor(iface); + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/ConnectionProxy.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/ConnectionProxy.java new file mode 100644 index 00000000..72a09567 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/ConnectionProxy.java @@ -0,0 +1,300 @@ +package com.codingapi.springboot.authorization.jdbc.proxy; + +import com.codingapi.springboot.authorization.interceptor.SQLInterceptState; +import com.codingapi.springboot.authorization.interceptor.SQLRunningContext; + +import java.sql.*; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; + +public class ConnectionProxy implements Connection { + + private final Connection connection; + + public ConnectionProxy(Connection connection) { + this.connection = connection; + } + + private SQLInterceptState interceptState; + + @Override + public Statement createStatement() throws SQLException { + return new StatementProxy(connection.createStatement(), interceptState); + } + + @Override + public PreparedStatement prepareStatement(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return new PreparedStatementProxy(connection.prepareStatement(interceptState.getSql()),interceptState); + } + + @Override + public CallableStatement prepareCall(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return new CallableStatementProxy(connection.prepareCall(interceptState.getSql()), interceptState); + } + + @Override + public String nativeSQL(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return connection.nativeSQL(interceptState.getSql()); + } + + @Override + public void setAutoCommit(boolean autoCommit) throws SQLException { + connection.setAutoCommit(autoCommit); + } + + @Override + public boolean getAutoCommit() throws SQLException { + return connection.getAutoCommit(); + } + + @Override + public void commit() throws SQLException { + connection.commit(); + } + + @Override + public void rollback() throws SQLException { + connection.rollback(); + } + + @Override + public void close() throws SQLException { + connection.close(); + } + + @Override + public boolean isClosed() throws SQLException { + return connection.isClosed(); + } + + @Override + public DatabaseMetaData getMetaData() throws SQLException { + return connection.getMetaData(); + } + + @Override + public void setReadOnly(boolean readOnly) throws SQLException { + connection.setReadOnly(readOnly); + } + + @Override + public boolean isReadOnly() throws SQLException { + return connection.isReadOnly(); + } + + @Override + public void setCatalog(String catalog) throws SQLException { + connection.setCatalog(catalog); + } + + @Override + public String getCatalog() throws SQLException { + return connection.getCatalog(); + } + + @Override + public void setTransactionIsolation(int level) throws SQLException { + connection.setTransactionIsolation(level); + } + + @Override + public int getTransactionIsolation() throws SQLException { + return connection.getTransactionIsolation(); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return connection.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + connection.clearWarnings(); + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + return new StatementProxy(connection.createStatement(resultSetType, resultSetConcurrency),this.interceptState); + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return new PreparedStatementProxy(connection.prepareStatement(interceptState.getSql(), resultSetType, resultSetConcurrency),interceptState); + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return new CallableStatementProxy(connection.prepareCall(interceptState.getSql(), resultSetType, resultSetConcurrency),interceptState); + } + + @Override + public Map> getTypeMap() throws SQLException { + return connection.getTypeMap(); + } + + @Override + public void setTypeMap(Map> map) throws SQLException { + connection.setTypeMap(map); + } + + @Override + public void setHoldability(int holdability) throws SQLException { + connection.setHoldability(holdability); + } + + @Override + public int getHoldability() throws SQLException { + return connection.getHoldability(); + } + + @Override + public Savepoint setSavepoint() throws SQLException { + return connection.setSavepoint(); + } + + @Override + public Savepoint setSavepoint(String name) throws SQLException { + return connection.setSavepoint(name); + } + + @Override + public void rollback(Savepoint savepoint) throws SQLException { + connection.rollback(savepoint); + } + + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + connection.releaseSavepoint(savepoint); + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return new StatementProxy(connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability),interceptState); + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return new PreparedStatementProxy(connection.prepareStatement(interceptState.getSql(), resultSetType, resultSetConcurrency, resultSetHoldability),interceptState); + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return new CallableStatementProxy(connection.prepareCall(interceptState.getSql(), resultSetType, resultSetConcurrency, resultSetHoldability),interceptState); + } + + @Override + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return new PreparedStatementProxy(connection.prepareStatement(interceptState.getSql(), autoGeneratedKeys),interceptState); + } + + @Override + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return new PreparedStatementProxy(connection.prepareStatement(interceptState.getSql(), columnIndexes),interceptState); + } + + @Override + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return new PreparedStatementProxy(connection.prepareStatement(interceptState.getSql(), columnNames),interceptState); + } + + @Override + public Clob createClob() throws SQLException { + return connection.createClob(); + } + + @Override + public Blob createBlob() throws SQLException { + return connection.createBlob(); + } + + @Override + public NClob createNClob() throws SQLException { + return connection.createNClob(); + } + + @Override + public SQLXML createSQLXML() throws SQLException { + return connection.createSQLXML(); + } + + @Override + public boolean isValid(int timeout) throws SQLException { + return connection.isValid(timeout); + } + + @Override + public void setClientInfo(String name, String value) throws SQLClientInfoException { + connection.setClientInfo(name, value); + } + + @Override + public void setClientInfo(Properties properties) throws SQLClientInfoException { + connection.setClientInfo(properties); + } + + @Override + public String getClientInfo(String name) throws SQLException { + return connection.getClientInfo(name); + } + + @Override + public Properties getClientInfo() throws SQLException { + return connection.getClientInfo(); + } + + @Override + public Array createArrayOf(String typeName, Object[] elements) throws SQLException { + return connection.createArrayOf(typeName, elements); + } + + @Override + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + return connection.createStruct(typeName, attributes); + } + + @Override + public void setSchema(String schema) throws SQLException { + connection.setSchema(schema); + } + + @Override + public String getSchema() throws SQLException { + return connection.getSchema(); + } + + @Override + public void abort(Executor executor) throws SQLException { + connection.abort(executor); + } + + @Override + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + connection.setNetworkTimeout(executor, milliseconds); + } + + @Override + public int getNetworkTimeout() throws SQLException { + return connection.getNetworkTimeout(); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return connection.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return connection.isWrapperFor(iface); + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/PreparedStatementProxy.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/PreparedStatementProxy.java new file mode 100644 index 00000000..89c17acd --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/PreparedStatementProxy.java @@ -0,0 +1,585 @@ +package com.codingapi.springboot.authorization.jdbc.proxy; + +import com.codingapi.springboot.authorization.interceptor.SQLInterceptState; +import com.codingapi.springboot.authorization.interceptor.SQLRunningContext; +import lombok.AllArgsConstructor; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.*; +import java.util.Calendar; + +@AllArgsConstructor +public class PreparedStatementProxy implements PreparedStatement { + + private final PreparedStatement preparedStatement; + + private SQLInterceptState interceptState; + + + @Override + public ResultSet executeQuery() throws SQLException { + return new ResultSetProxy(preparedStatement.executeQuery(),interceptState); + } + + @Override + public int executeUpdate() throws SQLException { + return preparedStatement.executeUpdate(); + } + + @Override + public void setNull(int parameterIndex, int sqlType) throws SQLException { + preparedStatement.setNull(parameterIndex, sqlType); + } + + @Override + public void setBoolean(int parameterIndex, boolean x) throws SQLException { + preparedStatement.setBoolean(parameterIndex, x); + } + + @Override + public void setByte(int parameterIndex, byte x) throws SQLException { + preparedStatement.setByte(parameterIndex, x); + } + + @Override + public void setShort(int parameterIndex, short x) throws SQLException { + preparedStatement.setShort(parameterIndex, x); + } + + @Override + public void setInt(int parameterIndex, int x) throws SQLException { + preparedStatement.setInt(parameterIndex, x); + } + + @Override + public void setLong(int parameterIndex, long x) throws SQLException { + preparedStatement.setLong(parameterIndex, x); + } + + @Override + public void setFloat(int parameterIndex, float x) throws SQLException { + preparedStatement.setFloat(parameterIndex, x); + } + + @Override + public void setDouble(int parameterIndex, double x) throws SQLException { + preparedStatement.setDouble(parameterIndex, x); + } + + @Override + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { + preparedStatement.setBigDecimal(parameterIndex, x); + } + + @Override + public void setString(int parameterIndex, String x) throws SQLException { + preparedStatement.setString(parameterIndex, x); + } + + @Override + public void setBytes(int parameterIndex, byte[] x) throws SQLException { + preparedStatement.setBytes(parameterIndex, x); + } + + @Override + public void setDate(int parameterIndex, Date x) throws SQLException { + preparedStatement.setDate(parameterIndex, x); + } + + @Override + public void setTime(int parameterIndex, Time x) throws SQLException { + preparedStatement.setTime(parameterIndex, x); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { + preparedStatement.setTimestamp(parameterIndex, x); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + preparedStatement.setAsciiStream(parameterIndex, x, length); + } + + @Override + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + preparedStatement.setUnicodeStream(parameterIndex, x, length); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + preparedStatement.setBinaryStream(parameterIndex, x, length); + } + + @Override + public void clearParameters() throws SQLException { + preparedStatement.clearParameters(); + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { + preparedStatement.setObject(parameterIndex, x, targetSqlType); + } + + @Override + public void setObject(int parameterIndex, Object x) throws SQLException { + preparedStatement.setObject(parameterIndex, x); + } + + @Override + public boolean execute() throws SQLException { + return preparedStatement.execute(); + } + + @Override + public void addBatch() throws SQLException { + preparedStatement.addBatch(); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { + preparedStatement.setCharacterStream(parameterIndex, reader, length); + } + + @Override + public void setRef(int parameterIndex, Ref x) throws SQLException { + preparedStatement.setRef(parameterIndex, x); + } + + @Override + public void setBlob(int parameterIndex, Blob x) throws SQLException { + preparedStatement.setBlob(parameterIndex, x); + } + + @Override + public void setClob(int parameterIndex, Clob x) throws SQLException { + preparedStatement.setClob(parameterIndex, x); + } + + @Override + public void setArray(int parameterIndex, Array x) throws SQLException { + preparedStatement.setArray(parameterIndex, x); + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + return preparedStatement.getMetaData(); + } + + @Override + public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { + preparedStatement.setDate(parameterIndex, x, cal); + } + + @Override + public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { + preparedStatement.setTime(parameterIndex, x, cal); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { + preparedStatement.setTimestamp(parameterIndex, x, cal); + } + + @Override + public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { + preparedStatement.setNull(parameterIndex, sqlType, typeName); + } + + @Override + public void setURL(int parameterIndex, URL x) throws SQLException { + preparedStatement.setURL(parameterIndex, x); + } + + @Override + public ParameterMetaData getParameterMetaData() throws SQLException { + return preparedStatement.getParameterMetaData(); + } + + @Override + public void setRowId(int parameterIndex, RowId x) throws SQLException { + preparedStatement.setRowId(parameterIndex, x); + } + + @Override + public void setNString(int parameterIndex, String value) throws SQLException { + preparedStatement.setNString(parameterIndex, value); + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { + preparedStatement.setNCharacterStream(parameterIndex, value, length); + } + + @Override + public void setNClob(int parameterIndex, NClob value) throws SQLException { + preparedStatement.setNClob(parameterIndex, value); + } + + @Override + public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { + preparedStatement.setClob(parameterIndex, reader, length); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { + preparedStatement.setBlob(parameterIndex, inputStream, length); + } + + @Override + public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + preparedStatement.setNClob(parameterIndex, reader, length); + } + + @Override + public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + preparedStatement.setSQLXML(parameterIndex, xmlObject); + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException { + preparedStatement.setObject(parameterIndex, x, targetSqlType, scaleOrLength); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { + preparedStatement.setAsciiStream(parameterIndex, x, length); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { + preparedStatement.setBinaryStream(parameterIndex, x, length); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + preparedStatement.setCharacterStream(parameterIndex, reader, length); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { + preparedStatement.setAsciiStream(parameterIndex, x); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { + preparedStatement.setBinaryStream(parameterIndex, x); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { + preparedStatement.setCharacterStream(parameterIndex, reader); + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { + preparedStatement.setNCharacterStream(parameterIndex, value); + } + + @Override + public void setClob(int parameterIndex, Reader reader) throws SQLException { + preparedStatement.setClob(parameterIndex, reader); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { + preparedStatement.setBlob(parameterIndex, inputStream); + } + + @Override + public void setNClob(int parameterIndex, Reader reader) throws SQLException { + preparedStatement.setNClob(parameterIndex, reader); + } + + @Override + public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + preparedStatement.setObject(parameterIndex, x, targetSqlType, scaleOrLength); + } + + @Override + public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException { + preparedStatement.setObject(parameterIndex, x, targetSqlType); + } + + @Override + public long executeLargeUpdate() throws SQLException { + return preparedStatement.executeLargeUpdate(); + } + + @Override + public ResultSet executeQuery(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return new ResultSetProxy(preparedStatement.executeQuery(interceptState.getSql()),interceptState); + } + + @Override + public int executeUpdate(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return preparedStatement.executeUpdate(interceptState.getSql()); + } + + @Override + public void close() throws SQLException { + preparedStatement.close(); + } + + @Override + public int getMaxFieldSize() throws SQLException { + return preparedStatement.getMaxFieldSize(); + } + + @Override + public void setMaxFieldSize(int max) throws SQLException { + preparedStatement.setMaxFieldSize(max); + } + + @Override + public int getMaxRows() throws SQLException { + return preparedStatement.getMaxRows(); + } + + @Override + public void setMaxRows(int max) throws SQLException { + preparedStatement.setMaxRows(max); + } + + @Override + public void setEscapeProcessing(boolean enable) throws SQLException { + preparedStatement.setEscapeProcessing(enable); + } + + @Override + public int getQueryTimeout() throws SQLException { + return preparedStatement.getQueryTimeout(); + } + + @Override + public void setQueryTimeout(int seconds) throws SQLException { + preparedStatement.setQueryTimeout(seconds); + } + + @Override + public void cancel() throws SQLException { + preparedStatement.cancel(); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return preparedStatement.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + preparedStatement.clearWarnings(); + } + + @Override + public void setCursorName(String name) throws SQLException { + preparedStatement.setCursorName(name); + } + + @Override + public boolean execute(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return preparedStatement.execute(interceptState.getSql()); + } + + @Override + public ResultSet getResultSet() throws SQLException { + return new ResultSetProxy(preparedStatement.getResultSet(),interceptState); + } + + @Override + public int getUpdateCount() throws SQLException { + return preparedStatement.getUpdateCount(); + } + + @Override + public boolean getMoreResults() throws SQLException { + return preparedStatement.getMoreResults(); + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + preparedStatement.setFetchDirection(direction); + } + + @Override + public int getFetchDirection() throws SQLException { + return preparedStatement.getFetchDirection(); + } + + @Override + public void setFetchSize(int rows) throws SQLException { + preparedStatement.setFetchSize(rows); + } + + @Override + public int getFetchSize() throws SQLException { + return preparedStatement.getFetchSize(); + } + + @Override + public int getResultSetConcurrency() throws SQLException { + return preparedStatement.getResultSetConcurrency(); + } + + @Override + public int getResultSetType() throws SQLException { + return preparedStatement.getResultSetType(); + } + + @Override + public void addBatch(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + preparedStatement.addBatch(interceptState.getSql()); + } + + @Override + public void clearBatch() throws SQLException { + preparedStatement.clearBatch(); + } + + @Override + public int[] executeBatch() throws SQLException { + return preparedStatement.executeBatch(); + } + + @Override + public Connection getConnection() throws SQLException { + return new ConnectionProxy(preparedStatement.getConnection()); + } + + @Override + public boolean getMoreResults(int current) throws SQLException { + return preparedStatement.getMoreResults(current); + } + + @Override + public ResultSet getGeneratedKeys() throws SQLException { + return new ResultSetProxy(preparedStatement.getGeneratedKeys(),interceptState); + } + + @Override + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return preparedStatement.executeUpdate(interceptState.getSql(), autoGeneratedKeys); + } + + @Override + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return preparedStatement.executeUpdate(interceptState.getSql(), columnIndexes); + } + + @Override + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return preparedStatement.executeUpdate(interceptState.getSql(), columnNames); + } + + @Override + public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return preparedStatement.execute(interceptState.getSql(), autoGeneratedKeys); + } + + @Override + public boolean execute(String sql, int[] columnIndexes) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return preparedStatement.execute(interceptState.getSql(), columnIndexes); + } + + @Override + public boolean execute(String sql, String[] columnNames) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return preparedStatement.execute(interceptState.getSql(), columnNames); + } + + @Override + public int getResultSetHoldability() throws SQLException { + return preparedStatement.getResultSetHoldability(); + } + + @Override + public boolean isClosed() throws SQLException { + return preparedStatement.isClosed(); + } + + @Override + public void setPoolable(boolean poolable) throws SQLException { + preparedStatement.setPoolable(poolable); + } + + @Override + public boolean isPoolable() throws SQLException { + return preparedStatement.isPoolable(); + } + + @Override + public void closeOnCompletion() throws SQLException { + preparedStatement.closeOnCompletion(); + } + + @Override + public boolean isCloseOnCompletion() throws SQLException { + return preparedStatement.isCloseOnCompletion(); + } + + @Override + public long getLargeUpdateCount() throws SQLException { + return preparedStatement.getLargeUpdateCount(); + } + + @Override + public void setLargeMaxRows(long max) throws SQLException { + preparedStatement.setLargeMaxRows(max); + } + + @Override + public long getLargeMaxRows() throws SQLException { + return preparedStatement.getLargeMaxRows(); + } + + @Override + public long[] executeLargeBatch() throws SQLException { + return preparedStatement.executeLargeBatch(); + } + + @Override + public long executeLargeUpdate(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return preparedStatement.executeLargeUpdate(interceptState.getSql()); + } + + @Override + public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return preparedStatement.executeLargeUpdate(interceptState.getSql(), autoGeneratedKeys); + } + + @Override + public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return preparedStatement.executeLargeUpdate(interceptState.getSql(), columnIndexes); + } + + @Override + public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return preparedStatement.executeLargeUpdate(interceptState.getSql(), columnNames); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return preparedStatement.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return preparedStatement.isWrapperFor(iface); + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/ResultSetProxy.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/ResultSetProxy.java new file mode 100644 index 00000000..40c72b09 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/ResultSetProxy.java @@ -0,0 +1,1180 @@ +package com.codingapi.springboot.authorization.jdbc.proxy; + +import com.codingapi.springboot.authorization.handler.ColumnHandlerContext; +import com.codingapi.springboot.authorization.interceptor.SQLInterceptState; +import lombok.extern.slf4j.Slf4j; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.*; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +public class ResultSetProxy implements ResultSet { + + private final ResultSet resultSet; + private final ResultSetMetaData metaData; + private final SQLInterceptState interceptState; + + private final Map columnLabelMap = new HashMap<>(); + + public ResultSetProxy(ResultSet resultSet, SQLInterceptState interceptState) throws SQLException { + this.resultSet = resultSet; + this.metaData = resultSet.getMetaData(); + this.interceptState = interceptState; + int columnCount = metaData.getColumnCount(); + for (int i = 1; i <= columnCount; i++) { + String columnLabel = metaData.getColumnLabel(i); + columnLabelMap.put(columnLabel.toUpperCase(), i); + } + } + + @Override + public boolean next() throws SQLException { + return resultSet.next(); + } + + @Override + public void close() throws SQLException { + resultSet.close(); + } + + @Override + public boolean wasNull() throws SQLException { + return resultSet.wasNull(); + } + + @Override + public String getString(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getString(interceptState, columnIndex, tableName, columnName, resultSet.getString(columnIndex)); + } + + @Override + public boolean getBoolean(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getBoolean(interceptState, columnIndex, tableName, columnName, resultSet.getBoolean(columnIndex)); + } + + @Override + public byte getByte(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getByte(interceptState, columnIndex, tableName, columnName, resultSet.getByte(columnIndex)); + } + + @Override + public short getShort(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getShort(interceptState, columnIndex, tableName, columnName, resultSet.getShort(columnIndex)); + } + + @Override + public int getInt(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getInt(interceptState, columnIndex, tableName, columnName, resultSet.getInt(columnIndex)); + } + + @Override + public long getLong(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getLong(interceptState, columnIndex, tableName, columnName, resultSet.getLong(columnIndex)); + } + + @Override + public float getFloat(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getFloat(interceptState, columnIndex, tableName, columnName, resultSet.getFloat(columnIndex)); + } + + @Override + public double getDouble(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getDouble(interceptState, columnIndex, tableName, columnName, resultSet.getDouble(columnIndex)); + } + + @Override + public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getBigDecimal(interceptState, columnIndex, tableName, columnName, resultSet.getBigDecimal(columnIndex, scale)); + } + + @Override + public byte[] getBytes(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getBytes(interceptState, columnIndex, tableName, columnName, resultSet.getBytes(columnIndex)); + } + + @Override + public Date getDate(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getDate(interceptState, columnIndex, tableName, columnName, resultSet.getDate(columnIndex)); + } + + @Override + public Time getTime(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getTime(interceptState, columnIndex, tableName, columnName, resultSet.getTime(columnIndex)); + } + + @Override + public Timestamp getTimestamp(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getTimestamp(interceptState, columnIndex, tableName, columnName, resultSet.getTimestamp(columnIndex)); + } + + @Override + public InputStream getAsciiStream(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getAsciiStream(interceptState, columnIndex, tableName, columnName, resultSet.getAsciiStream(columnIndex)); + } + + @Override + public InputStream getUnicodeStream(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getUnicodeStream(interceptState, columnIndex, tableName, columnName, resultSet.getUnicodeStream(columnIndex)); + } + + @Override + public InputStream getBinaryStream(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getBinaryStream(interceptState, columnIndex, tableName, columnName, resultSet.getBinaryStream(columnIndex)); + } + + @Override + public String getString(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getString(interceptState, columnIndex, tableName, columnName, resultSet.getString(columnIndex)); + } + + @Override + public boolean getBoolean(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getBoolean(interceptState, columnIndex, tableName, columnName, resultSet.getBoolean(columnIndex)); + } + + @Override + public byte getByte(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getByte(interceptState, columnIndex, tableName, columnName, resultSet.getByte(columnIndex)); + } + + @Override + public short getShort(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getShort(interceptState, columnIndex, tableName, columnName, resultSet.getShort(columnIndex)); + } + + @Override + public int getInt(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getInt(interceptState, columnIndex, tableName, columnName, resultSet.getInt(columnIndex)); + } + + @Override + public long getLong(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getLong(interceptState, columnIndex, tableName, columnName, resultSet.getLong(columnIndex)); + } + + @Override + public float getFloat(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getFloat(interceptState, columnIndex, tableName, columnName, resultSet.getFloat(columnIndex)); + } + + @Override + public double getDouble(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getDouble(interceptState, columnIndex, tableName, columnName, resultSet.getDouble(columnIndex)); + } + + @Override + public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getBigDecimal(interceptState, columnIndex, tableName, columnName, resultSet.getBigDecimal(columnIndex, scale)); + } + + @Override + public byte[] getBytes(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getBytes(interceptState, columnIndex, tableName, columnName, resultSet.getBytes(columnIndex)); + } + + @Override + public Date getDate(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getDate(interceptState, columnIndex, tableName, columnName, resultSet.getDate(columnIndex)); + } + + @Override + public Time getTime(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getTime(interceptState, columnIndex, tableName, columnName, resultSet.getTime(columnIndex)); + } + + @Override + public Timestamp getTimestamp(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getTimestamp(interceptState, columnIndex, tableName, columnName, resultSet.getTimestamp(columnIndex)); + } + + @Override + public InputStream getAsciiStream(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getAsciiStream(interceptState, columnIndex, tableName, columnName, resultSet.getAsciiStream(columnIndex)); + } + + @Override + public InputStream getUnicodeStream(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getUnicodeStream(interceptState, columnIndex, tableName, columnName, resultSet.getUnicodeStream(columnIndex)); + } + + @Override + public InputStream getBinaryStream(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getBinaryStream(interceptState, columnIndex, tableName, columnName, resultSet.getBinaryStream(columnIndex)); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return resultSet.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + resultSet.clearWarnings(); + } + + @Override + public String getCursorName() throws SQLException { + return resultSet.getCursorName(); + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + return resultSet.getMetaData(); + } + + @Override + public Object getObject(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getObject(interceptState, columnIndex, tableName, columnName, resultSet.getObject(columnIndex)); + } + + @Override + public Object getObject(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getObject(interceptState, columnIndex, tableName, columnName, resultSet.getObject(columnIndex)); + } + + @Override + public int findColumn(String columnLabel) throws SQLException { + return resultSet.findColumn(columnLabel); + } + + @Override + public Reader getCharacterStream(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getCharacterStream(interceptState, columnIndex, tableName, columnName, resultSet.getCharacterStream(columnIndex)); + } + + @Override + public Reader getCharacterStream(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getCharacterStream(interceptState, columnIndex, tableName, columnName, resultSet.getCharacterStream(columnIndex)); + } + + @Override + public BigDecimal getBigDecimal(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getBigDecimal(interceptState, columnIndex, tableName, columnName, resultSet.getBigDecimal(columnIndex)); + } + + @Override + public BigDecimal getBigDecimal(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getBigDecimal(interceptState, columnIndex, tableName, columnName, resultSet.getBigDecimal(columnIndex)); + } + + @Override + public boolean isBeforeFirst() throws SQLException { + return resultSet.isBeforeFirst(); + } + + @Override + public boolean isAfterLast() throws SQLException { + return resultSet.isAfterLast(); + } + + @Override + public boolean isFirst() throws SQLException { + return resultSet.isFirst(); + } + + @Override + public boolean isLast() throws SQLException { + return resultSet.isLast(); + } + + @Override + public void beforeFirst() throws SQLException { + resultSet.beforeFirst(); + } + + @Override + public void afterLast() throws SQLException { + resultSet.afterLast(); + } + + @Override + public boolean first() throws SQLException { + return resultSet.first(); + } + + @Override + public boolean last() throws SQLException { + return resultSet.last(); + } + + @Override + public int getRow() throws SQLException { + return resultSet.getRow(); + } + + @Override + public boolean absolute(int row) throws SQLException { + return resultSet.absolute(row); + } + + @Override + public boolean relative(int rows) throws SQLException { + return resultSet.relative(rows); + } + + @Override + public boolean previous() throws SQLException { + return resultSet.previous(); + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + resultSet.setFetchDirection(direction); + } + + @Override + public int getFetchDirection() throws SQLException { + return resultSet.getFetchDirection(); + } + + @Override + public void setFetchSize(int rows) throws SQLException { + resultSet.setFetchSize(rows); + } + + @Override + public int getFetchSize() throws SQLException { + return resultSet.getFetchSize(); + } + + @Override + public int getType() throws SQLException { + return resultSet.getType(); + } + + @Override + public int getConcurrency() throws SQLException { + return resultSet.getConcurrency(); + } + + @Override + public boolean rowUpdated() throws SQLException { + return resultSet.rowUpdated(); + } + + @Override + public boolean rowInserted() throws SQLException { + return resultSet.rowInserted(); + } + + @Override + public boolean rowDeleted() throws SQLException { + return resultSet.rowDeleted(); + } + + @Override + public void updateNull(int columnIndex) throws SQLException { + resultSet.updateNull(columnIndex); + } + + @Override + public void updateBoolean(int columnIndex, boolean x) throws SQLException { + resultSet.updateBoolean(columnIndex, x); + } + + @Override + public void updateByte(int columnIndex, byte x) throws SQLException { + resultSet.updateByte(columnIndex, x); + } + + @Override + public void updateShort(int columnIndex, short x) throws SQLException { + resultSet.updateShort(columnIndex, x); + } + + @Override + public void updateInt(int columnIndex, int x) throws SQLException { + resultSet.updateInt(columnIndex, x); + } + + @Override + public void updateLong(int columnIndex, long x) throws SQLException { + resultSet.updateLong(columnIndex, x); + } + + @Override + public void updateFloat(int columnIndex, float x) throws SQLException { + resultSet.updateFloat(columnIndex, x); + } + + @Override + public void updateDouble(int columnIndex, double x) throws SQLException { + resultSet.updateDouble(columnIndex, x); + } + + @Override + public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { + resultSet.updateBigDecimal(columnIndex, x); + } + + @Override + public void updateString(int columnIndex, String x) throws SQLException { + resultSet.updateString(columnIndex, x); + } + + @Override + public void updateBytes(int columnIndex, byte[] x) throws SQLException { + resultSet.updateBytes(columnIndex, x); + } + + @Override + public void updateDate(int columnIndex, Date x) throws SQLException { + resultSet.updateDate(columnIndex, x); + } + + @Override + public void updateTime(int columnIndex, Time x) throws SQLException { + resultSet.updateTime(columnIndex, x); + } + + @Override + public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { + resultSet.updateTimestamp(columnIndex, x); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException { + resultSet.updateAsciiStream(columnIndex, x, length); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException { + resultSet.updateBinaryStream(columnIndex, x, length); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException { + resultSet.updateCharacterStream(columnIndex, x, length); + } + + @Override + public void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException { + resultSet.updateObject(columnIndex, x, scaleOrLength); + } + + @Override + public void updateObject(int columnIndex, Object x) throws SQLException { + resultSet.updateObject(columnIndex, x); + } + + @Override + public void updateNull(String columnLabel) throws SQLException { + resultSet.updateNull(columnLabel); + } + + @Override + public void updateBoolean(String columnLabel, boolean x) throws SQLException { + resultSet.updateBoolean(columnLabel, x); + } + + @Override + public void updateByte(String columnLabel, byte x) throws SQLException { + resultSet.updateByte(columnLabel, x); + } + + @Override + public void updateShort(String columnLabel, short x) throws SQLException { + resultSet.updateShort(columnLabel, x); + } + + @Override + public void updateInt(String columnLabel, int x) throws SQLException { + resultSet.updateInt(columnLabel, x); + } + + @Override + public void updateLong(String columnLabel, long x) throws SQLException { + resultSet.updateLong(columnLabel, x); + } + + @Override + public void updateFloat(String columnLabel, float x) throws SQLException { + resultSet.updateFloat(columnLabel, x); + } + + @Override + public void updateDouble(String columnLabel, double x) throws SQLException { + resultSet.updateDouble(columnLabel, x); + } + + @Override + public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException { + resultSet.updateBigDecimal(columnLabel, x); + } + + @Override + public void updateString(String columnLabel, String x) throws SQLException { + resultSet.updateString(columnLabel, x); + } + + @Override + public void updateBytes(String columnLabel, byte[] x) throws SQLException { + resultSet.updateBytes(columnLabel, x); + } + + @Override + public void updateDate(String columnLabel, Date x) throws SQLException { + resultSet.updateDate(columnLabel, x); + } + + @Override + public void updateTime(String columnLabel, Time x) throws SQLException { + resultSet.updateTime(columnLabel, x); + } + + @Override + public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException { + resultSet.updateTimestamp(columnLabel, x); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x, int length) throws SQLException { + resultSet.updateAsciiStream(columnLabel, x, length); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x, int length) throws SQLException { + resultSet.updateBinaryStream(columnLabel, x, length); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader, int length) throws SQLException { + resultSet.updateCharacterStream(columnLabel, reader, length); + } + + @Override + public void updateObject(String columnLabel, Object x, int scaleOrLength) throws SQLException { + resultSet.updateObject(columnLabel, x, scaleOrLength); + } + + @Override + public void updateObject(String columnLabel, Object x) throws SQLException { + resultSet.updateObject(columnLabel, x); + } + + @Override + public void insertRow() throws SQLException { + resultSet.insertRow(); + } + + @Override + public void updateRow() throws SQLException { + resultSet.updateRow(); + } + + @Override + public void deleteRow() throws SQLException { + resultSet.deleteRow(); + } + + @Override + public void refreshRow() throws SQLException { + resultSet.refreshRow(); + } + + @Override + public void cancelRowUpdates() throws SQLException { + resultSet.cancelRowUpdates(); + } + + @Override + public void moveToInsertRow() throws SQLException { + resultSet.moveToInsertRow(); + } + + @Override + public void moveToCurrentRow() throws SQLException { + resultSet.moveToCurrentRow(); + } + + @Override + public Statement getStatement() throws SQLException { + return resultSet.getStatement(); + } + + @Override + public Object getObject(int columnIndex, Map> map) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getObject(interceptState, columnIndex, tableName, columnName, resultSet.getDate(columnIndex)); + } + + @Override + public Ref getRef(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getRef(interceptState, columnIndex, tableName, columnName, resultSet.getRef(columnIndex)); + } + + @Override + public Blob getBlob(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getBlob(interceptState, columnIndex, tableName, columnName, resultSet.getBlob(columnIndex)); + } + + @Override + public Clob getClob(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getClob(interceptState, columnIndex, tableName, columnName, resultSet.getClob(columnIndex)); + } + + @Override + public Array getArray(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getArray(interceptState, columnIndex, tableName, columnName, resultSet.getArray(columnIndex)); + } + + @Override + public Object getObject(String columnLabel, Map> map) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getObject(interceptState, columnIndex, tableName, columnName, resultSet.getObject(columnIndex)); + } + + @Override + public Ref getRef(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getRef(interceptState, columnIndex, tableName, columnName, resultSet.getRef(columnIndex)); + } + + @Override + public Blob getBlob(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getBlob(interceptState, columnIndex, tableName, columnName, resultSet.getBlob(columnIndex)); + } + + @Override + public Clob getClob(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getClob(interceptState, columnIndex, tableName, columnName, resultSet.getClob(columnIndex)); + } + + @Override + public Array getArray(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getArray(interceptState, columnIndex, tableName, columnName, resultSet.getArray(columnIndex)); + } + + @Override + public Date getDate(int columnIndex, Calendar cal) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getDate(interceptState, columnIndex, tableName, columnName, resultSet.getDate(columnIndex, cal)); + } + + @Override + public Date getDate(String columnLabel, Calendar cal) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getDate(interceptState, columnIndex, tableName, columnName, resultSet.getDate(columnIndex, cal)); + } + + @Override + public Time getTime(int columnIndex, Calendar cal) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getTime(interceptState, columnIndex, tableName, columnName, resultSet.getTime(columnIndex, cal)); + } + + @Override + public Time getTime(String columnLabel, Calendar cal) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getTime(interceptState, columnIndex, tableName, columnName, resultSet.getTime(columnIndex, cal)); + } + + @Override + public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getTimestamp(interceptState, columnIndex, tableName, columnName, resultSet.getTimestamp(columnIndex, cal)); + } + + @Override + public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getTimestamp(interceptState, columnIndex, tableName, columnName, resultSet.getTimestamp(columnIndex, cal)); + } + + @Override + public URL getURL(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getURL(interceptState, columnIndex, tableName, columnName, resultSet.getURL(columnIndex)); + } + + @Override + public URL getURL(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getURL(interceptState, columnIndex, tableName, columnName, resultSet.getURL(columnIndex)); + } + + @Override + public void updateRef(int columnIndex, Ref x) throws SQLException { + resultSet.updateRef(columnIndex, x); + } + + @Override + public void updateRef(String columnLabel, Ref x) throws SQLException { + resultSet.updateRef(columnLabel, x); + } + + @Override + public void updateBlob(int columnIndex, Blob x) throws SQLException { + resultSet.updateBlob(columnIndex, x); + } + + @Override + public void updateBlob(String columnLabel, Blob x) throws SQLException { + resultSet.updateBlob(columnLabel, x); + } + + @Override + public void updateClob(int columnIndex, Clob x) throws SQLException { + resultSet.updateClob(columnIndex, x); + } + + @Override + public void updateClob(String columnLabel, Clob x) throws SQLException { + resultSet.updateClob(columnLabel, x); + } + + @Override + public void updateArray(int columnIndex, Array x) throws SQLException { + resultSet.updateArray(columnIndex, x); + } + + @Override + public void updateArray(String columnLabel, Array x) throws SQLException { + resultSet.updateArray(columnLabel, x); + } + + @Override + public RowId getRowId(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getRowId(interceptState, columnIndex, tableName, columnName, resultSet.getRowId(columnIndex)); + } + + @Override + public RowId getRowId(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getRowId(interceptState, columnIndex, tableName, columnName, resultSet.getRowId(columnIndex)); + } + + @Override + public void updateRowId(int columnIndex, RowId x) throws SQLException { + resultSet.updateRowId(columnIndex, x); + } + + @Override + public void updateRowId(String columnLabel, RowId x) throws SQLException { + resultSet.updateRowId(columnLabel, x); + } + + @Override + public int getHoldability() throws SQLException { + return resultSet.getHoldability(); + } + + @Override + public boolean isClosed() throws SQLException { + return resultSet.isClosed(); + } + + @Override + public void updateNString(int columnIndex, String nString) throws SQLException { + resultSet.updateNString(columnIndex, nString); + } + + @Override + public void updateNString(String columnLabel, String nString) throws SQLException { + resultSet.updateNString(columnLabel, nString); + } + + @Override + public void updateNClob(int columnIndex, NClob nClob) throws SQLException { + resultSet.updateNClob(columnIndex, nClob); + } + + @Override + public void updateNClob(String columnLabel, NClob nClob) throws SQLException { + resultSet.updateNClob(columnLabel, nClob); + } + + @Override + public NClob getNClob(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getNClob(interceptState, columnIndex, tableName, columnName, resultSet.getNClob(columnIndex)); + } + + @Override + public NClob getNClob(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getNClob(interceptState, columnIndex, tableName, columnName, resultSet.getNClob(columnIndex)); + } + + @Override + public SQLXML getSQLXML(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getSQLXML(interceptState, columnIndex, tableName, columnName, resultSet.getSQLXML(columnIndex)); + } + + @Override + public SQLXML getSQLXML(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getSQLXML(interceptState, columnIndex, tableName, columnName, resultSet.getSQLXML(columnIndex)); + } + + @Override + public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException { + resultSet.updateSQLXML(columnIndex, xmlObject); + } + + @Override + public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException { + resultSet.updateSQLXML(columnLabel, xmlObject); + } + + @Override + public String getNString(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getNString(interceptState, columnIndex, tableName, columnName, resultSet.getNString(columnIndex)); + } + + @Override + public String getNString(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getNString(interceptState, columnIndex, tableName, columnName, resultSet.getNString(columnIndex)); + } + + @Override + public Reader getNCharacterStream(int columnIndex) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getNCharacterStream(interceptState, columnIndex, tableName, columnName, resultSet.getNCharacterStream(columnIndex)); + } + + @Override + public Reader getNCharacterStream(String columnLabel) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getNCharacterStream(interceptState, columnIndex, tableName, columnName, resultSet.getNCharacterStream(columnIndex)); + } + + @Override + public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + resultSet.updateNCharacterStream(columnIndex, x, length); + } + + @Override + public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + resultSet.updateNCharacterStream(columnLabel, reader, length); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException { + resultSet.updateAsciiStream(columnIndex, x, length); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException { + resultSet.updateBinaryStream(columnIndex, x, length); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + resultSet.updateCharacterStream(columnIndex, x, length); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException { + resultSet.updateAsciiStream(columnLabel, x, length); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException { + resultSet.updateBinaryStream(columnLabel, x, length); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + resultSet.updateCharacterStream(columnLabel, reader, length); + } + + @Override + public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException { + resultSet.updateBlob(columnIndex, inputStream, length); + } + + @Override + public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException { + resultSet.updateBlob(columnLabel, inputStream, length); + } + + @Override + public void updateClob(int columnIndex, Reader reader, long length) throws SQLException { + resultSet.updateClob(columnIndex, reader, length); + } + + @Override + public void updateClob(String columnLabel, Reader reader, long length) throws SQLException { + resultSet.updateClob(columnLabel, reader, length); + } + + @Override + public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException { + resultSet.updateNClob(columnIndex, reader, length); + } + + @Override + public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException { + resultSet.updateNClob(columnLabel, reader, length); + } + + @Override + public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException { + resultSet.updateNCharacterStream(columnIndex, x); + } + + @Override + public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { + resultSet.updateNCharacterStream(columnLabel, reader); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException { + resultSet.updateAsciiStream(columnIndex, x); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException { + resultSet.updateBinaryStream(columnIndex, x); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x) throws SQLException { + resultSet.updateCharacterStream(columnIndex, x); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException { + resultSet.updateAsciiStream(columnLabel, x); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException { + resultSet.updateBinaryStream(columnLabel, x); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { + resultSet.updateCharacterStream(columnLabel, reader); + } + + @Override + public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException { + resultSet.updateBlob(columnIndex, inputStream); + } + + @Override + public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException { + resultSet.updateBlob(columnLabel, inputStream); + } + + @Override + public void updateClob(int columnIndex, Reader reader) throws SQLException { + resultSet.updateClob(columnIndex, reader); + } + + @Override + public void updateClob(String columnLabel, Reader reader) throws SQLException { + resultSet.updateClob(columnLabel, reader); + } + + @Override + public void updateNClob(int columnIndex, Reader reader) throws SQLException { + resultSet.updateNClob(columnIndex, reader); + } + + @Override + public void updateNClob(String columnLabel, Reader reader) throws SQLException { + resultSet.updateNClob(columnLabel, reader); + } + + @Override + public T getObject(int columnIndex, Class type) throws SQLException { + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getObject(interceptState, columnIndex, tableName, columnName, resultSet.getObject(columnIndex, type), type); + } + + @Override + public T getObject(String columnLabel, Class type) throws SQLException { + int columnIndex = columnLabelMap.get(columnLabel.toUpperCase()); + String tableName = metaData.getTableName(columnIndex); + String columnName = metaData.getColumnName(columnIndex); + return ColumnHandlerContext.getInstance().getObject(interceptState, columnIndex, tableName, columnName, resultSet.getObject(columnIndex, type), type); + } + + @Override + public void updateObject(int columnIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + resultSet.updateObject(columnIndex, x, targetSqlType, scaleOrLength); + } + + @Override + public void updateObject(String columnLabel, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + resultSet.updateObject(columnLabel, x, targetSqlType, scaleOrLength); + } + + @Override + public void updateObject(int columnIndex, Object x, SQLType targetSqlType) throws SQLException { + resultSet.updateObject(columnIndex, x, targetSqlType); + } + + @Override + public void updateObject(String columnLabel, Object x, SQLType targetSqlType) throws SQLException { + resultSet.updateObject(columnLabel, x, targetSqlType); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return resultSet.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return resultSet.isWrapperFor(iface); + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/StatementProxy.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/StatementProxy.java new file mode 100644 index 00000000..99af9be9 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/jdbc/proxy/StatementProxy.java @@ -0,0 +1,290 @@ +package com.codingapi.springboot.authorization.jdbc.proxy; + +import com.codingapi.springboot.authorization.interceptor.SQLInterceptState; +import com.codingapi.springboot.authorization.interceptor.SQLRunningContext; +import lombok.AllArgsConstructor; + +import java.sql.*; + +@AllArgsConstructor +public class StatementProxy implements Statement { + + private final Statement statement; + private SQLInterceptState interceptState; + + + + @Override + public ResultSet executeQuery(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return new ResultSetProxy(statement.executeQuery(interceptState.getSql()), interceptState); + } + + @Override + public int executeUpdate(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return statement.executeUpdate(interceptState.getSql()); + } + + @Override + public void close() throws SQLException { + statement.close(); + } + + @Override + public int getMaxFieldSize() throws SQLException { + return statement.getMaxFieldSize(); + } + + @Override + public void setMaxFieldSize(int max) throws SQLException { + statement.setMaxFieldSize(max); + } + + @Override + public int getMaxRows() throws SQLException { + return statement.getMaxRows(); + } + + @Override + public void setMaxRows(int max) throws SQLException { + statement.setMaxRows(max); + } + + @Override + public void setEscapeProcessing(boolean enable) throws SQLException { + statement.setEscapeProcessing(enable); + } + + @Override + public int getQueryTimeout() throws SQLException { + return statement.getQueryTimeout(); + } + + @Override + public void setQueryTimeout(int seconds) throws SQLException { + statement.setQueryTimeout(seconds); + } + + @Override + public void cancel() throws SQLException { + statement.cancel(); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return statement.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + statement.clearWarnings(); + } + + @Override + public void setCursorName(String name) throws SQLException { + statement.setCursorName(name); + } + + @Override + public boolean execute(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return statement.execute(interceptState.getSql()); + } + + @Override + public ResultSet getResultSet() throws SQLException { + return new ResultSetProxy(statement.getResultSet(), interceptState); + } + + @Override + public int getUpdateCount() throws SQLException { + return statement.getUpdateCount(); + } + + @Override + public boolean getMoreResults() throws SQLException { + return statement.getMoreResults(); + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + statement.setFetchDirection(direction); + } + + @Override + public int getFetchDirection() throws SQLException { + return statement.getFetchDirection(); + } + + @Override + public void setFetchSize(int rows) throws SQLException { + statement.setFetchSize(rows); + } + + @Override + public int getFetchSize() throws SQLException { + return statement.getFetchSize(); + } + + @Override + public int getResultSetConcurrency() throws SQLException { + return statement.getResultSetConcurrency(); + } + + @Override + public int getResultSetType() throws SQLException { + return statement.getResultSetType(); + } + + @Override + public void addBatch(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + statement.addBatch(interceptState.getSql()); + } + + @Override + public void clearBatch() throws SQLException { + statement.clearBatch(); + } + + @Override + public int[] executeBatch() throws SQLException { + return statement.executeBatch(); + } + + @Override + public Connection getConnection() throws SQLException { + return new ConnectionProxy(statement.getConnection()); + } + + @Override + public boolean getMoreResults(int current) throws SQLException { + return statement.getMoreResults(current); + } + + @Override + public ResultSet getGeneratedKeys() throws SQLException { + return new ResultSetProxy(statement.getGeneratedKeys(),this.interceptState); + } + + @Override + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return statement.executeUpdate(interceptState.getSql(), autoGeneratedKeys); + } + + @Override + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return statement.executeUpdate(interceptState.getSql(), columnIndexes); + } + + @Override + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return statement.executeUpdate(interceptState.getSql(), columnNames); + } + + @Override + public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return statement.execute(interceptState.getSql(), autoGeneratedKeys); + } + + @Override + public boolean execute(String sql, int[] columnIndexes) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return statement.execute(interceptState.getSql(), columnIndexes); + } + + @Override + public boolean execute(String sql, String[] columnNames) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return statement.execute(interceptState.getSql(), columnNames); + } + + @Override + public int getResultSetHoldability() throws SQLException { + return statement.getResultSetHoldability(); + } + + @Override + public boolean isClosed() throws SQLException { + return statement.isClosed(); + } + + @Override + public void setPoolable(boolean poolable) throws SQLException { + statement.setPoolable(poolable); + } + + @Override + public boolean isPoolable() throws SQLException { + return statement.isPoolable(); + } + + @Override + public void closeOnCompletion() throws SQLException { + statement.closeOnCompletion(); + } + + @Override + public boolean isCloseOnCompletion() throws SQLException { + return statement.isCloseOnCompletion(); + } + + @Override + public long getLargeUpdateCount() throws SQLException { + return statement.getLargeUpdateCount(); + } + + @Override + public void setLargeMaxRows(long max) throws SQLException { + statement.setLargeMaxRows(max); + } + + @Override + public long getLargeMaxRows() throws SQLException { + return statement.getLargeMaxRows(); + } + + @Override + public long[] executeLargeBatch() throws SQLException { + return statement.executeLargeBatch(); + } + + @Override + public long executeLargeUpdate(String sql) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return statement.executeLargeUpdate(interceptState.getSql()); + } + + @Override + public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return statement.executeLargeUpdate(interceptState.getSql(), autoGeneratedKeys); + } + + @Override + public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return statement.executeLargeUpdate(interceptState.getSql(), columnIndexes); + } + + @Override + public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException { + this.interceptState = SQLRunningContext.getInstance().intercept(sql); + return statement.executeLargeUpdate(interceptState.getSql(), columnNames); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return statement.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return statement.isWrapperFor(iface); + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/ColumnMask.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/ColumnMask.java new file mode 100644 index 00000000..c0721ad0 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/ColumnMask.java @@ -0,0 +1,12 @@ +package com.codingapi.springboot.authorization.mask; + +/** + * 列数据脱敏 + */ +public interface ColumnMask { + + boolean support(Object value); + + Object mask(Object value); + +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/ColumnMaskContext.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/ColumnMaskContext.java new file mode 100644 index 00000000..5e84d63a --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/ColumnMaskContext.java @@ -0,0 +1,34 @@ +package com.codingapi.springboot.authorization.mask; + +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +public class ColumnMaskContext { + + private final List columnMasks; + + private ColumnMaskContext() { + this.columnMasks = new ArrayList<>(); + } + + public void addColumnMask(ColumnMask columnMask) { + this.columnMasks.add(columnMask); + } + + @Getter + private final static ColumnMaskContext instance = new ColumnMaskContext(); + + + public T mask(T value) { + for (ColumnMask columnMask : columnMasks) { + if (columnMask.support(value)) { + return (T)columnMask.mask(value); + } + } + return value; + } + + +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/impl/BankCardMask.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/impl/BankCardMask.java new file mode 100644 index 00000000..2aba7e1b --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/impl/BankCardMask.java @@ -0,0 +1,43 @@ +package com.codingapi.springboot.authorization.mask.impl; + +import com.codingapi.springboot.authorization.mask.ColumnMask; + +import java.util.regex.Pattern; + +/** + * 银行卡脱敏 + */ +public class BankCardMask implements ColumnMask { + + private final static Pattern BANK_CARD_MATCHER_PATTERN = Pattern.compile("^\\d{13,19}$"); + private final static Pattern BANK_CARD_MASK_PATTERN = Pattern.compile("(\\d{6})\\d{3,9}(\\d{4})"); + + @Override + public boolean support(Object value) { + if (value instanceof String) { + return BANK_CARD_MATCHER_PATTERN.matcher((String) value).matches(); + } + return false; + } + + @Override + public Object mask(Object value) { + if (value instanceof String) { + String bankCard = (String) value; + int length = bankCard.length(); + // 获取字符串的长度 + int maskLength = length - 10; + + // 手动构造星号部分 + StringBuilder maskedPart = new StringBuilder(maskLength); + for (int i = 0; i < maskLength; i++) { + maskedPart.append("*"); + } + + // 用构造好的星号替换原始字符串中的部分内容 + return BANK_CARD_MASK_PATTERN.matcher((String) value) + .replaceAll("$1" + maskedPart + "$2"); + } + return value; + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/impl/IDCardMask.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/impl/IDCardMask.java new file mode 100644 index 00000000..51541ad9 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/impl/IDCardMask.java @@ -0,0 +1,29 @@ +package com.codingapi.springboot.authorization.mask.impl; + +import com.codingapi.springboot.authorization.mask.ColumnMask; + +import java.util.regex.Pattern; + +/** + * 身份证脱敏 + */ +public class IDCardMask implements ColumnMask { + private final static Pattern ID_CARD_MATCHER_PATTERN = Pattern.compile("^(\\d{15}|\\d{18}|\\d{17}[Xx])$"); + private final static Pattern ID_CARD_MASK_PATTERN = Pattern.compile("(\\d{6})\\d{8}(\\w{4})"); + + @Override + public boolean support(Object value) { + if (value instanceof String) { + return ID_CARD_MATCHER_PATTERN.matcher((String) value).matches(); + } + return false; + } + + @Override + public Object mask(Object value) { + if (value instanceof String) { + return ID_CARD_MASK_PATTERN.matcher( (String) value).replaceAll("$1********$2"); + } + return value; + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/impl/PhoneMask.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/impl/PhoneMask.java new file mode 100644 index 00000000..3903a82f --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/mask/impl/PhoneMask.java @@ -0,0 +1,31 @@ +package com.codingapi.springboot.authorization.mask.impl; + +import com.codingapi.springboot.authorization.mask.ColumnMask; + +import java.util.regex.Pattern; + +/** + * 电话号码脱敏 + */ +public class PhoneMask implements ColumnMask { + + private static final Pattern PHONE_MATCHER_PATTERN = Pattern.compile("^1[3-9]\\d{9}$"); + private static final Pattern PHONE_MASK_PATTERN = Pattern.compile("(\\d{3})\\d{4}(\\d{4})"); + + @Override + public boolean support(Object value) { + if (value instanceof String) { + return PHONE_MATCHER_PATTERN.matcher((String) value).matches(); + } + return false; + } + + @Override + public Object mask(Object value) { + if (value instanceof String) { + return PHONE_MASK_PATTERN.matcher((String) value).replaceAll("$1****$2"); + } + return value; + } + +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/properties/DataAuthorizationProperties.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/properties/DataAuthorizationProperties.java new file mode 100644 index 00000000..48bfe771 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/properties/DataAuthorizationProperties.java @@ -0,0 +1,17 @@ +package com.codingapi.springboot.authorization.properties; + + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class DataAuthorizationProperties { + + private boolean showSql = false; + + public DataAuthorizationProperties() { + DataAuthorizationPropertyContext.getInstance().setDataAuthorizationProperties(this); + } + +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/properties/DataAuthorizationPropertyContext.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/properties/DataAuthorizationPropertyContext.java new file mode 100644 index 00000000..dcb5f582 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/properties/DataAuthorizationPropertyContext.java @@ -0,0 +1,24 @@ +package com.codingapi.springboot.authorization.properties; + +import lombok.Getter; + +public class DataAuthorizationPropertyContext { + + @Getter + private final static DataAuthorizationPropertyContext instance = new DataAuthorizationPropertyContext(); + + private DataAuthorizationPropertyContext(){} + + private DataAuthorizationProperties dataAuthorizationProperties; + + protected void setDataAuthorizationProperties(DataAuthorizationProperties dataAuthorizationProperties){ + this.dataAuthorizationProperties = dataAuthorizationProperties; + } + + public boolean showSql(){ + if(dataAuthorizationProperties!=null) { + return dataAuthorizationProperties.isShowSql(); + } + return false; + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/register/ConditionHandlerRegister.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/register/ConditionHandlerRegister.java new file mode 100644 index 00000000..d39fb34d --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/register/ConditionHandlerRegister.java @@ -0,0 +1,14 @@ +package com.codingapi.springboot.authorization.register; + + +import com.codingapi.springboot.authorization.handler.RowHandler; +import com.codingapi.springboot.authorization.handler.RowHandlerContext; + +public class ConditionHandlerRegister { + + public ConditionHandlerRegister(RowHandler rowHandler) { + if (rowHandler != null) { + RowHandlerContext.getInstance().setRowHandler(rowHandler); + } + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/register/DataAuthorizationContextRegister.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/register/DataAuthorizationContextRegister.java new file mode 100644 index 00000000..39ca7096 --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/register/DataAuthorizationContextRegister.java @@ -0,0 +1,17 @@ +package com.codingapi.springboot.authorization.register; + +import com.codingapi.springboot.authorization.DataAuthorizationContext; +import com.codingapi.springboot.authorization.filter.DataAuthorizationFilter; + +import java.util.List; + +public class DataAuthorizationContextRegister { + + public DataAuthorizationContextRegister(List dataAuthorizationFilters) { + if(dataAuthorizationFilters!=null) { + for (DataAuthorizationFilter filter : dataAuthorizationFilters) { + DataAuthorizationContext.getInstance().addDataAuthorizationFilter(filter); + } + } + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/register/ResultSetHandlerRegister.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/register/ResultSetHandlerRegister.java new file mode 100644 index 00000000..6070269a --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/register/ResultSetHandlerRegister.java @@ -0,0 +1,15 @@ +package com.codingapi.springboot.authorization.register; + + +import com.codingapi.springboot.authorization.handler.ColumnHandler; +import com.codingapi.springboot.authorization.handler.ColumnHandlerContext; + +public class ResultSetHandlerRegister { + + public ResultSetHandlerRegister(ColumnHandler columnHandler){ + if(columnHandler !=null) { + ColumnHandlerContext.getInstance().setColumnHandler(columnHandler); + } + } + +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/register/SQLInterceptorRegister.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/register/SQLInterceptorRegister.java new file mode 100644 index 00000000..57534f0d --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/register/SQLInterceptorRegister.java @@ -0,0 +1,14 @@ +package com.codingapi.springboot.authorization.register; + + +import com.codingapi.springboot.authorization.interceptor.SQLInterceptor; +import com.codingapi.springboot.authorization.interceptor.SQLInterceptorContext; + +public class SQLInterceptorRegister { + + public SQLInterceptorRegister(SQLInterceptor sqlInterceptor) { + if(sqlInterceptor!=null) { + SQLInterceptorContext.getInstance().setSqlInterceptor(sqlInterceptor); + } + } +} diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/utils/SQLUtils.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/utils/SQLUtils.java new file mode 100644 index 00000000..817da94c --- /dev/null +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/utils/SQLUtils.java @@ -0,0 +1,25 @@ +package com.codingapi.springboot.authorization.utils; + +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.select.Select; + +public class SQLUtils { + + /** + * 判断是否为查询 + */ + public static boolean isQuerySql(String sql) { + if (sql == null || sql.trim().isEmpty()) { + return false; // 空字符串或 null 不是有效 SQL + } + try { + Statement statement = CCJSqlParserUtil.parse(sql); + return statement instanceof Select; + } catch (Exception e) { + return false; + } + } + + +} diff --git a/springboot-starter-data-authorization/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/springboot-starter-data-authorization/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..77aba71d --- /dev/null +++ b/springboot-starter-data-authorization/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.codingapi.springboot.authorization.DataAuthorizationConfiguration diff --git a/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/DataAuthorizationContextTest.java b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/DataAuthorizationContextTest.java new file mode 100644 index 00000000..3dd25040 --- /dev/null +++ b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/DataAuthorizationContextTest.java @@ -0,0 +1,378 @@ +package com.codingapi.springboot.authorization; + +import com.codingapi.springboot.authorization.current.CurrentUser; +import com.codingapi.springboot.authorization.enhancer.DataPermissionSQLEnhancer; +import com.codingapi.springboot.authorization.entity.Depart; +import com.codingapi.springboot.authorization.entity.Unit; +import com.codingapi.springboot.authorization.entity.User; +import com.codingapi.springboot.authorization.filter.DefaultDataAuthorizationFilter; +import com.codingapi.springboot.authorization.handler.Condition; +import com.codingapi.springboot.authorization.handler.RowHandler; +import com.codingapi.springboot.authorization.interceptor.SQLRunningContext; +import com.codingapi.springboot.authorization.mask.ColumnMaskContext; +import com.codingapi.springboot.authorization.mask.impl.BankCardMask; +import com.codingapi.springboot.authorization.mask.impl.IDCardMask; +import com.codingapi.springboot.authorization.mask.impl.PhoneMask; +import com.codingapi.springboot.authorization.repository.DepartRepository; +import com.codingapi.springboot.authorization.repository.UnitRepository; +import com.codingapi.springboot.authorization.repository.UserRepository; +import lombok.extern.slf4j.Slf4j; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.select.SelectItem; +import net.sf.jsqlparser.statement.select.SelectItemVisitor; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.test.annotation.Rollback; + +import java.time.LocalDate; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Slf4j +@SpringBootTest +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@Rollback(value = false) +public class DataAuthorizationContextTest { + + @Autowired + private UserRepository userRepository; + @Autowired + private DepartRepository departRepository; + @Autowired + private UnitRepository unitRepository; + @Autowired + private JdbcTemplate jdbcTemplate; + + @Test + @Order(1) + void test1() { + + unitRepository.deleteAll(); + departRepository.deleteAll(); + userRepository.deleteAll(); + + + DataAuthorizationContext.getInstance().clearDataAuthorizationFilters(); + + DataAuthorizationContext.getInstance().addDataAuthorizationFilter(new DefaultDataAuthorizationFilter() { + + @Override + public boolean supportRowAuthorization(String tableName, String tableAlias) { + User user = CurrentUser.getInstance().getUser(); + // 模拟仅当用户为lorne时,才进行行级过滤 + return user.getName().equalsIgnoreCase("bob"); + } + + @Override + public Condition rowAuthorization(String tableName, String tableAlias) { + if (tableName.equalsIgnoreCase("t_unit")) { + long unitId = CurrentUser.getInstance().getUser().getUnitId(); + String conditionTemplate = "%s.id = " + unitId; + return Condition.formatCondition(conditionTemplate, tableAlias); + } + if (tableName.equalsIgnoreCase("t_depart")) { + long departId = CurrentUser.getInstance().getUser().getDepartId(); + String conditionTemplate = "%s.id = " + departId; + + // 在条件处理的过程中,执行的查询都将不会被拦截 + List departs = departRepository.findAll(); + log.info("departs:{}", departs); + assertEquals(2, departs.size()); + + return Condition.formatCondition(conditionTemplate, tableAlias); + } + if (tableName.equalsIgnoreCase("t_user")) { + long departId = CurrentUser.getInstance().getUser().getDepartId(); + String conditionTemplate = "%s.depart_id = " + departId; + return Condition.formatCondition(conditionTemplate, tableAlias); + } + return null; + } + + }); + + Unit rootUnit = new Unit("Coding总公司"); + unitRepository.save(rootUnit); + + Unit sdUnit = new Unit("Coding山东分公司", rootUnit.getId()); + unitRepository.save(sdUnit); + + Depart jgbDepart = new Depart("Coding架构部", rootUnit.getId()); + departRepository.save(jgbDepart); + + Depart xmbDepart = new Depart("Coding项目部", sdUnit.getId()); + departRepository.save(xmbDepart); + + + User lorne = new User("lorne", LocalDate.parse("1991-01-01"), "beijing", "110105199003078999", "13812345678", jgbDepart); + User bob = new User("bob", LocalDate.parse("1991-01-01"), "beijing", "110105199003078999", "13812345678", xmbDepart); + User tom = new User("tom", LocalDate.parse("1991-01-01"), "beijing", "110105199003078999", "13812345678", xmbDepart); + + userRepository.save(lorne); + userRepository.save(bob); + userRepository.save(tom); + + + CurrentUser.getInstance().setUser(bob); + + + PageRequest request = PageRequest.of(0, 100); + Page users = userRepository.findAll(request); + + + System.out.println(users.getTotalElements()); + users.forEach(System.out::println); + + + assertEquals(2, users.getTotalElements()); + assertEquals(2, userRepository.count()); + assertEquals(1, departRepository.count()); + assertEquals(1, unitRepository.count()); + + } + + + @Test + @Order(2) + void test2() { + + unitRepository.deleteAll(); + departRepository.deleteAll(); + userRepository.deleteAll(); + + + ColumnMaskContext.getInstance().addColumnMask(new IDCardMask()); + ColumnMaskContext.getInstance().addColumnMask(new PhoneMask()); + ColumnMaskContext.getInstance().addColumnMask(new BankCardMask()); + + DataAuthorizationContext.getInstance().clearDataAuthorizationFilters(); + + DataAuthorizationContext.getInstance().addDataAuthorizationFilter(new DefaultDataAuthorizationFilter() { + @Override + public T columnAuthorization(String tableName, String columnName, T value) { + return ColumnMaskContext.getInstance().mask(value); + } + + @Override + public boolean supportColumnAuthorization(String tableName, String columnName, Object value) { + User user = CurrentUser.getInstance().getUser(); + return user != null && user.getName().equalsIgnoreCase("bob"); + } + + }); + + Unit rootUnit = new Unit("Coding总公司"); + unitRepository.save(rootUnit); + + Unit sdUnit = new Unit("Coding山东分公司", rootUnit.getId()); + unitRepository.save(sdUnit); + + Depart jgbDepart = new Depart("Coding架构部", rootUnit.getId()); + departRepository.save(jgbDepart); + + Depart xmbDepart = new Depart("Coding项目部", sdUnit.getId()); + departRepository.save(xmbDepart); + + + User lorne = new User("lorne", LocalDate.parse("1991-01-01"), "beijing", "110105199003078999", "13812345678", jgbDepart); + User bob = new User("bob", LocalDate.parse("1991-01-01"), "beijing", "110105199003078999", "13812345678", xmbDepart); + User tom = new User("tom", LocalDate.parse("1991-01-01"), "beijing", "110105199003078999", "13812345678", xmbDepart); + + userRepository.save(lorne); + userRepository.save(bob); + userRepository.save(tom); + + assertTrue(SQLRunningContext.getInstance().skipDataAuthorization(() -> userRepository.findAll()).size() >= 3); + + CurrentUser.getInstance().setUser(bob); + + PageRequest request = PageRequest.of(0, 100); + Page users = userRepository.findAll(request); + assertTrue(users.getTotalElements() >= 3); + + for (User user : users) { + assertEquals("138****5678", user.getPhone()); + } + + CurrentUser.getInstance().setUser(lorne); + + users = userRepository.findAll(request); + assertTrue(users.getTotalElements() >= 3); + + for (User user : users) { + assertEquals("13812345678", user.getPhone()); + } + + + } + + + @Test + @Order(3) + void test3() { + + unitRepository.deleteAll(); + departRepository.deleteAll(); + userRepository.deleteAll(); + + ColumnMaskContext.getInstance().addColumnMask(new IDCardMask()); + ColumnMaskContext.getInstance().addColumnMask(new PhoneMask()); + ColumnMaskContext.getInstance().addColumnMask(new BankCardMask()); + + DataAuthorizationContext.getInstance().clearDataAuthorizationFilters(); + + DataAuthorizationContext.getInstance().addDataAuthorizationFilter(new DefaultDataAuthorizationFilter() { + @Override + public T columnAuthorization(String tableName, String columnName, T value) { + return ColumnMaskContext.getInstance().mask(value); + } + + @Override + public boolean supportColumnAuthorization(String tableName, String columnName, Object value) { + return true; + } + + }); + + Unit rootUnit = new Unit("Coding总公司"); + unitRepository.save(rootUnit); + + Unit sdUnit = new Unit("Coding山东分公司", rootUnit.getId()); + unitRepository.save(sdUnit); + + Depart jgbDepart = new Depart("Coding架构部", rootUnit.getId()); + departRepository.save(jgbDepart); + + Depart xmbDepart = new Depart("Coding项目部", sdUnit.getId()); + departRepository.save(xmbDepart); + + User lorne = new User("lorne", LocalDate.parse("1991-01-01"), "beijing", "110105199003078999", "13812345678", jgbDepart); + User bob = new User("bob", LocalDate.parse("1991-01-01"), "beijing", "110105199003078999", "13812345678", xmbDepart); + User tom = new User("tom", LocalDate.parse("1991-01-01"), "beijing", "110105199003078999", "13812345678", xmbDepart); + + userRepository.save(lorne); + userRepository.save(bob); + userRepository.save(tom); + + List> users = jdbcTemplate.queryForList("select * from t_user"); + System.out.println(users); + assertEquals(3, users.size()); + + for (Map user : users) { + assertEquals("138****5678", user.get("phone")); + } + + } + + +// @Test + @Order(4) + void test4() throws Exception{ + String sql = "SELECT\n" + + "\tt.* \n" + + "FROM\n" + + "\t(\n" + + "\t\tSELECT\n" + + "\t\t\tUNYiV.id AS '历史工作经历编号',\n" + + "\t\t\tUNYiV.company_name AS '历史工作单位',\n" + + "\t\t\tUNYiV.depart_name AS '历史工作部门',\n" + + "\t\t\tUNYiV.post_name AS '历史工作岗位',\n" + + "\t\t\tUNYiV.start_date AS '开始时间',\n" + + "\t\t\tUNYiV.end_date AS '结束时间',\n" + + "\t\t\towasH.员工编号 AS '员工编号',\n" + + "\t\t\towasH.员工姓名 AS '员工姓名',\n" + + "\t\t\towasH.员工生日 AS '员工生日',\n" + + "\t\t\towasH.员工地址 AS '员工地址',\n" + + "\t\t\towasH.身份证号码 AS '身份证号码',\n" + + "\t\t\towasH.手机号 AS '手机号',\n" + + "\t\t\towasH.部门编号 AS '部门编号',\n" + + "\t\t\towasH.岗位编号 AS '岗位编号',\n" + + "\t\t\towasH.任现职编号 AS '任现职编号',\n" + + "\t\t\towasH.社团编号 AS '社团编号',\n" + + "\t\t\towasH.社团名称 AS '社团名称',\n" + + "\t\t\towasH.创建时间 AS '创建时间' \n" + + "\t\tFROM\n" + + "\t\t\tt_work AS pehMS,\n" + + "\t\t\tt_employee AS OGwG7,\n" + + "\t\t\tt_work_history AS UNYiV,\n" + + "\t\t\t(\n" + + "\t\t\t\tSELECT\n" + + "\t\t\t\t\tWXJj8.id AS '员工编号',\n" + + "\t\t\t\t\tWXJj8.NAME AS '员工姓名',\n" + + "\t\t\t\t\tWXJj8.birth_date AS '员工生日',\n" + + "\t\t\t\t\tWXJj8.address AS '员工地址',\n" + + "\t\t\t\t\tWXJj8.id_card AS '身份证号码',\n" + + "\t\t\t\t\tWXJj8.phone AS '手机号',\n" + + "\t\t\t\t\tWXJj8.depart_id AS '部门编号',\n" + + "\t\t\t\t\tWXJj8.post_id AS '岗位编号',\n" + + "\t\t\t\t\tWXJj8.work_id AS '任现职编号',\n" + + "\t\t\t\t\trnGD4.id AS '社团编号',\n" + + "\t\t\t\t\trnGD4.NAME AS '社团名称',\n" + + "\t\t\t\t\trnGD4.create_date AS '创建时间' \n" + + "\t\t\t\tFROM\n" + + "\t\t\t\t\tt_employee AS WXJj8,\n" + + "\t\t\t\t\tt_league_employee AS dEj96,\n" + + "\t\t\t\t\tt_league AS rnGD4 \n" + + "\t\t\t\tWHERE\n" + + "\t\t\t\t\trnGD4.id < 100 \n" + + "\t\t\t\t\tAND dEj96.employee_id = WXJj8.id \n" + + "\t\t\t\t\tAND dEj96.league_id = rnGD4.id \n" + + "\t\t\t\t\tAND 1 = 1 \n" + + "\t\t\t) AS owasH \n" + + "\t\tWHERE\n" + + "\t\t\tUNYiV.employee_id = OGwG7.id \n" + + "\t\t\tAND OGwG7.work_id = pehMS.id \n" + + "\t\t\tAND owasH.任现职编号 = pehMS.id \n" + + "\t\t\tAND 1 = 1 \n" + + "\t) AS t , t_employee AS e where t.员工编号 = e.id and e.id = 1"; + + + DataAuthorizationContext.getInstance().clearDataAuthorizationFilters(); + DataAuthorizationContext.getInstance().addDataAuthorizationFilter(new DefaultDataAuthorizationFilter() { + @Override + public Condition rowAuthorization(String tableName, String tableAlias) { + return super.rowAuthorization(tableName, tableAlias); + } + + @Override + public T columnAuthorization(String tableName, String columnName, T value) { + System.out.println("tableName:" + tableName + ",columnName:" + columnName + ",value:" + value); + return value; + } + + @Override + public boolean supportColumnAuthorization(String tableName, String columnName, Object value) { + return true; + } + + @Override + public boolean supportRowAuthorization(String tableName, String tableAlias) { + return true; + } + }); + + + List> data = jdbcTemplate.queryForList(sql); +// System.out.println(data); + } + + + + +} diff --git a/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/DataAuthorizationTestApplication.java b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/DataAuthorizationTestApplication.java new file mode 100644 index 00000000..0760ec6a --- /dev/null +++ b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/DataAuthorizationTestApplication.java @@ -0,0 +1,13 @@ +package com.codingapi.springboot.authorization; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DataAuthorizationTestApplication { + + public static void main(String[] args) { + SpringApplication.run(DataAuthorizationConfiguration.class,args); + } + +} diff --git a/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/analyzer/SelectSQLAnalyzerTest.java b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/analyzer/SelectSQLAnalyzerTest.java new file mode 100644 index 00000000..ce2309f6 --- /dev/null +++ b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/analyzer/SelectSQLAnalyzerTest.java @@ -0,0 +1,198 @@ +package com.codingapi.springboot.authorization.analyzer; + +import com.codingapi.springboot.authorization.enhancer.DataPermissionSQLEnhancer; +import com.codingapi.springboot.authorization.handler.Condition; +import com.codingapi.springboot.authorization.handler.RowHandler; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.sql.SQLException; + +import static org.junit.jupiter.api.Assertions.*; + +class SelectSQLAnalyzerTest { + + + @Test + void test1() throws SQLException { + String sql = "select t1.*,t2.* from (SELECT * FROM t_employee as a2 WHERE id = 100 ) t1 ," + + " (SELECT * FROM t_employee as a1 ) t2 ," + + " (select * from t_employee a3 left join t_unit u on a3.unit_id = u.id ) t3 ," + + " (select 1 =1 ) as t4 " + + " limit 100"; + + RowHandler rowHandler = (subSql, tableName, tableAlias) -> { + if (tableName.equalsIgnoreCase("t_employee")) { + String conditionTemplate = "%s.id > 100 "; + return Condition.formatCondition(conditionTemplate, tableAlias); + } + return null; + }; + + DataPermissionSQLEnhancer builder = new DataPermissionSQLEnhancer(sql, rowHandler); + String newSql = builder.getNewSQL(); + System.out.println(newSql); + //SELECT t1.*, t2.* FROM (SELECT * FROM t_employee AS a2 WHERE a2.id > 100 AND id = 100) t1, + // (SELECT * FROM t_employee AS a1 WHERE a1.id > 100) t2, + // (SELECT * FROM t_employee a3 LEFT JOIN t_unit u ON a3.unit_id = u.id WHERE a3.id > 100) t3, + // (SELECT 1 = 1) AS t4 LIMIT 100 + assertEquals( + "SELECT t1.*, t2.* FROM (SELECT * FROM t_employee AS a2 WHERE a2.id > 100 AND id = 100) t1," + + " (SELECT * FROM t_employee AS a1 WHERE a1.id > 100) t2, " + + "(SELECT * FROM t_employee a3 LEFT JOIN t_unit u ON a3.unit_id = u.id WHERE a3.id > 100) t3, " + + "(SELECT 1 = 1) AS t4 LIMIT 100", newSql); + } + + @Test + void test2() throws SQLException { + String sql = "select e1_0.id,e1_0.address,e1_0.birth_date,e1_0.depart_id,e1_0.id_card,e1_0.name,e1_0.phone,e1_0.post_id,e1_0.work_id from t_employee e1_0 limit ?,?"; + + RowHandler rowHandler = (subSql, tableName, tableAlias) -> { + if (tableName.equalsIgnoreCase("t_employee")) { + String conditionTemplate = "%s.id > 100 "; + return Condition.formatCondition(conditionTemplate, tableAlias); + } + return null; + }; + + DataPermissionSQLEnhancer builder = new DataPermissionSQLEnhancer(sql, rowHandler); + String newSql = builder.getNewSQL(); + System.out.println(newSql); + assertEquals("SELECT e1_0.id, e1_0.address, e1_0.birth_date, e1_0.depart_id, e1_0.id_card, e1_0.name, e1_0.phone, e1_0.post_id, e1_0.work_id FROM t_employee e1_0 WHERE e1_0.id > 100 LIMIT ?, ?", newSql); } + + + @Test + void test3() throws SQLException { + String sql = "select aue1_0.ba_org_code from ba03_administrative_unit aue1_0 where aue1_0.ba_org_code like (?||'__') order by aue1_0.ba_org_code desc"; + + RowHandler rowHandler = (subSql, tableName, tableAlias) -> { + if (tableName.equalsIgnoreCase("ba03_administrative_unit")) { + String conditionTemplate = "%s.id > 100 "; + return Condition.formatCondition(conditionTemplate, tableAlias); + } + return null; + }; + + DataPermissionSQLEnhancer builder = new DataPermissionSQLEnhancer(sql, rowHandler); + String newSql = builder.getNewSQL(); + System.out.println(newSql); + assertEquals("SELECT aue1_0.ba_org_code FROM ba03_administrative_unit aue1_0 WHERE aue1_0.id > 100 AND aue1_0.ba_org_code LIKE (? || '__') ORDER BY aue1_0.ba_org_code DESC", newSql); + } + + @Test + void test4() throws SQLException{ + String sql = "SELECT\n" + + "\tUNYiV.id AS '历史工作经历编号',\n" + + "\tUNYiV.company_name AS '历史工作单位',\n" + + "\tUNYiV.depart_name AS '历史工作部门',\n" + + "\tUNYiV.post_name AS '历史工作岗位',\n" + + "\tUNYiV.start_date AS '开始时间',\n" + + "\tUNYiV.end_date AS '结束时间',\n" + + "\towasH.员工编号 AS '员工编号',\n" + + "\towasH.员工姓名 AS '员工姓名',\n" + + "\towasH.员工生日 AS '员工生日',\n" + + "\towasH.员工地址 AS '员工地址',\n" + + "\towasH.身份证号码 AS '身份证号码',\n" + + "\towasH.手机号 AS '手机号',\n" + + "\towasH.部门编号 AS '部门编号',\n" + + "\towasH.岗位编号 AS '岗位编号',\n" + + "\towasH.任现职编号 AS '任现职编号',\n" + + "\towasH.社团编号 AS '社团编号',\n" + + "\towasH.社团名称 AS '社团名称',\n" + + "\towasH.创建时间 AS '创建时间' \n" + + "FROM\n" + + "\tt_work AS pehMS,\n" + + "\tt_employee AS OGwG7,\n" + + "\tt_work_history AS UNYiV,\n" + + "\t(\n" + + "\t\tSELECT\n" + + "\t\t\tWXJj8.id AS '员工编号',\n" + + "\t\t\tWXJj8.NAME AS '员工姓名',\n" + + "\t\t\tWXJj8.birth_date AS '员工生日',\n" + + "\t\t\tWXJj8.address AS '员工地址',\n" + + "\t\t\tWXJj8.id_card AS '身份证号码',\n" + + "\t\t\tWXJj8.phone AS '手机号',\n" + + "\t\t\tWXJj8.depart_id AS '部门编号',\n" + + "\t\t\tWXJj8.post_id AS '岗位编号',\n" + + "\t\t\tWXJj8.work_id AS '任现职编号',\n" + + "\t\t\trnGD4.id AS '社团编号',\n" + + "\t\t\trnGD4.NAME AS '社团名称',\n" + + "\t\t\trnGD4.create_date AS '创建时间' \n" + + "\t\tFROM\n" + + "\t\t\tt_employee AS WXJj8,\n" + + "\t\t\tt_league_employee AS dEj96,\n" + + "\t\t\tt_league AS rnGD4 \n" + + "\t\tWHERE\n" + + "\t\t\tdEj96.employee_id = WXJj8.id \n" + + "\t\t\tAND dEj96.league_id = rnGD4.id \n" + + "\t\t\tAND 1 = 1 \n" + + "\t) AS owasH \n" + + "WHERE\n" + + "\tUNYiV.employee_id = OGwG7.id \n" + + "\tAND OGwG7.work_id = pehMS.id \n" + + "\tAND owasH.任现职编号 = pehMS.id \n" + + "\tAND 1 = 1"; + + + RowHandler rowHandler = (subSql, tableName, tableAlias) -> { + if (tableName.equalsIgnoreCase("t_league")) { + String conditionTemplate = "%s.id < 100 "; + return Condition.formatCondition(conditionTemplate, tableAlias); + } + return null; + }; + + DataPermissionSQLEnhancer builder = new DataPermissionSQLEnhancer(sql, rowHandler); + System.out.println(builder.getNewSQL()); + System.out.println(builder.getTableAlias());; + } + + @Test + @Order(5) + void test5() throws Exception{ + String sql = "SELECT next_val AS id_val FROM t_league_seq FOR UPDATE"; + RowHandler rowHandler = (subSql, tableName, tableAlias) -> { + if (tableName.equalsIgnoreCase("t_league")) { + String conditionTemplate = "%s.id < 100 "; + return Condition.formatCondition(conditionTemplate, tableAlias); + } + return null; + }; + DataPermissionSQLEnhancer builder = new DataPermissionSQLEnhancer(sql, rowHandler); + System.out.println(builder.getNewSQL()); + System.out.println(builder.getTableAlias());; + } + + + @Test + @Order(6) + void test6() throws Exception{ + String sql = "SELECT 1=1"; + RowHandler rowHandler = (subSql, tableName, tableAlias) -> { + if (tableName.equalsIgnoreCase("t_league")) { + String conditionTemplate = "%s.id < 100 "; + return Condition.formatCondition(conditionTemplate, tableAlias); + } + return null; + }; + DataPermissionSQLEnhancer builder = new DataPermissionSQLEnhancer(sql, rowHandler); + System.out.println(builder.getNewSQL()); + System.out.println(builder.getTableAlias());; + } + + @Test + @Order(7) + void test7() throws Exception{ + String sql = "SELECT * from t_employee"; + RowHandler rowHandler = (subSql, tableName, tableAlias) -> { + if (tableName.equalsIgnoreCase("t_employee")) { + String conditionTemplate = "%s.id < 100 "; + return Condition.formatCondition(conditionTemplate, tableAlias); + } + return null; + }; + DataPermissionSQLEnhancer builder = new DataPermissionSQLEnhancer(sql, rowHandler); + System.out.println(builder.getNewSQL()); + System.out.println(builder.getTableAlias());; + } +} diff --git a/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/current/CurrentUser.java b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/current/CurrentUser.java new file mode 100644 index 00000000..186c3d00 --- /dev/null +++ b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/current/CurrentUser.java @@ -0,0 +1,27 @@ +package com.codingapi.springboot.authorization.current; + +import com.codingapi.springboot.authorization.entity.User; +import lombok.Getter; + +public class CurrentUser { + + @Getter + private final static CurrentUser instance = new CurrentUser(); + + private final ThreadLocal threadLocal = new ThreadLocal<>(); + + private CurrentUser(){ + } + + public void setUser(User user){ + threadLocal.set(user); + } + + public User getUser(){ + return threadLocal.get(); + } + + public void remove(){ + threadLocal.remove(); + } +} diff --git a/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/entity/Depart.java b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/entity/Depart.java new file mode 100644 index 00000000..865d8801 --- /dev/null +++ b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/entity/Depart.java @@ -0,0 +1,38 @@ +package com.codingapi.springboot.authorization.entity; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +@Setter +@Getter +@Entity +@Table(name = "t_depart") +@NoArgsConstructor +public class Depart { + + @Id + @GeneratedValue + private long id; + + private String name; + + private long parentId; + + private long unitId; + + public Depart(String name, long unitId,long parentId) { + this.name = name; + this.parentId = parentId; + this.unitId = unitId; + } + + public Depart(String name,long unitId) { + this(name,unitId,0); + } +} diff --git a/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/entity/Unit.java b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/entity/Unit.java new file mode 100644 index 00000000..4ea4b0a7 --- /dev/null +++ b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/entity/Unit.java @@ -0,0 +1,34 @@ +package com.codingapi.springboot.authorization.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@Entity +@Table(name = "t_unit") +@NoArgsConstructor +public class Unit { + + @Id + @GeneratedValue + private long id; + + private String name; + + private long parentId; + + public Unit(String name, long parentId) { + this.name = name; + this.parentId = parentId; + } + + public Unit(String name) { + this(name,0); + } +} diff --git a/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/entity/User.java b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/entity/User.java new file mode 100644 index 00000000..04fca1f3 --- /dev/null +++ b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/entity/User.java @@ -0,0 +1,49 @@ +package com.codingapi.springboot.authorization.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import java.time.LocalDate; + +@Setter +@Getter +@Entity +@Table(name = "t_user") +@NoArgsConstructor +@ToString +public class User { + + @Id + @GeneratedValue + private long id; + + private String name; + + private LocalDate birthDate; + + private String address; + + private String idCard; + + private String phone; + + private long unitId; + + private long departId; + + public User(String name, LocalDate birthDate, String address, String idCard, String phone,Depart depart) { + this.name = name; + this.birthDate = birthDate; + this.address = address; + this.idCard = idCard; + this.phone = phone; + this.unitId = depart.getUnitId(); + this.departId = depart.getId(); + } +} diff --git a/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/mask/impl/BankCardMaskTest.java b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/mask/impl/BankCardMaskTest.java new file mode 100644 index 00000000..0b75e2e4 --- /dev/null +++ b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/mask/impl/BankCardMaskTest.java @@ -0,0 +1,17 @@ +package com.codingapi.springboot.authorization.mask.impl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class BankCardMaskTest { + + @Test + void test() { + String bankCard = "6222021001111111111"; + BankCardMask mask = new BankCardMask(); + assertTrue(mask.support(bankCard)); + assertEquals("622202*********1111", mask.mask(bankCard)); + } +} diff --git a/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/mask/impl/IDCardMaskTest.java b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/mask/impl/IDCardMaskTest.java new file mode 100644 index 00000000..3e758452 --- /dev/null +++ b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/mask/impl/IDCardMaskTest.java @@ -0,0 +1,16 @@ +package com.codingapi.springboot.authorization.mask.impl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class IDCardMaskTest { + + @Test + void test(){ + String idCard = "110101199003074012"; + IDCardMask mask = new IDCardMask(); + assertTrue(mask.support(idCard)); + assertEquals("110101********4012", mask.mask(idCard)); + } +} diff --git a/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/mask/impl/PhoneMaskTest.java b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/mask/impl/PhoneMaskTest.java new file mode 100644 index 00000000..d5c8d4f5 --- /dev/null +++ b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/mask/impl/PhoneMaskTest.java @@ -0,0 +1,17 @@ +package com.codingapi.springboot.authorization.mask.impl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class PhoneMaskTest { + + @Test + void test(){ + String phone = "15562581234"; + PhoneMask phoneMask = new PhoneMask(); + assertTrue(phoneMask.support(phone)); + assertEquals("155****1234", phoneMask.mask(phone)); + } + +} diff --git a/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/repository/DepartRepository.java b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/repository/DepartRepository.java new file mode 100644 index 00000000..b9c00b84 --- /dev/null +++ b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/repository/DepartRepository.java @@ -0,0 +1,8 @@ +package com.codingapi.springboot.authorization.repository; + +import com.codingapi.springboot.authorization.entity.Depart; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface DepartRepository extends JpaRepository { + +} diff --git a/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/repository/UnitRepository.java b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/repository/UnitRepository.java new file mode 100644 index 00000000..0bc6416d --- /dev/null +++ b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/repository/UnitRepository.java @@ -0,0 +1,8 @@ +package com.codingapi.springboot.authorization.repository; + +import com.codingapi.springboot.authorization.entity.Unit; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UnitRepository extends JpaRepository { + +} diff --git a/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/repository/UserRepository.java b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/repository/UserRepository.java new file mode 100644 index 00000000..f36df247 --- /dev/null +++ b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/repository/UserRepository.java @@ -0,0 +1,12 @@ +package com.codingapi.springboot.authorization.repository; + +import com.codingapi.springboot.authorization.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface UserRepository extends JpaRepository { + + + List findUserByDepartId(long departId); +} diff --git a/springboot-starter-data-authorization/src/test/resources/application.properties b/springboot-starter-data-authorization/src/test/resources/application.properties new file mode 100644 index 00000000..4e944839 --- /dev/null +++ b/springboot-starter-data-authorization/src/test/resources/application.properties @@ -0,0 +1,13 @@ + +spring.datasource.driver-class-name=com.codingapi.springboot.authorization.jdbc.AuthorizationJdbcDriver +spring.datasource.url=jdbc:h2:file:./test.db +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.show-sql=true + +#spring.datasource.driver-class-name=com.codingapi.springboot.authorization.jdbc.AuthorizationJdbcDriver +#spring.datasource.url=jdbc:mysql://localhost:3306/example?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true +#spring.datasource.username=root +#spring.datasource.password=lorne4j#2024 + +logging.level.com.codingapi.springboot.authorization=debug diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 8d142d1e..654d2dd8 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.14 + 2.9.15 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index b5fadf19..3255022e 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.14 + 2.9.15 springboot-starter-flow diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 0357b2d4..0b2c8a84 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.14 + 2.9.15 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index d8c0d844..afc5c315 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.14 + 2.9.15 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 23fb5d83..1c514784 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.14 +CodingApi SpringBoot-Starter 2.9.15 springboot version (${spring-boot.version}) ------------------------------------------------------ From ac787c144acb22193bf58c3b0864db39bd99111d Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Mon, 23 Dec 2024 12:00:12 +0800 Subject: [PATCH 074/101] fix reject back operator --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../flow/service/FlowNodeService.java | 14 +- .../springboot/flow/test/SignTest.java | 136 ++++++++++++++---- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 9 files changed, 126 insertions(+), 38 deletions(-) diff --git a/pom.xml b/pom.xml index 3ac27491..577ce5de 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.15 + 2.9.16 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 4be50638..68351e5c 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.15 + 2.9.16 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 654d2dd8..aa4522a0 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.15 + 2.9.16 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 3255022e..ecdd7f60 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.15 + 2.9.16 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java index b7210a8d..4d3a092f 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java @@ -18,6 +18,7 @@ import lombok.Getter; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -29,6 +30,7 @@ public class FlowNodeService { @Getter private FlowNode nextNode; private IFlowOperator nextOperator; + private IFlowOperator backOperator; private final FlowOperatorRepository flowOperatorRepository; private final FlowRecordRepository flowRecordRepository; @@ -116,6 +118,7 @@ public void loadDefaultBackNode(long parentRecordId) { } this.nextNode = nextNode; this.nextOperator = flowOperator; + this.backOperator = flowOperator; } @@ -138,6 +141,7 @@ public void loadCustomBackNode(FlowNode flowNode, long parentRecordId) { } this.nextNode = nextNode; this.nextOperator = flowOperator; + this.backOperator = flowOperator; } @@ -239,10 +243,16 @@ private List createNextRecord() { historyRecords); long workId = flowWork.getId(); - List operators = nextNode.loadFlowNodeOperator(flowSession, flowOperatorRepository); + List operators = null; + if (this.backOperator == null) { + operators = nextNode.loadFlowNodeOperator(flowSession, flowOperatorRepository); + } else { + operators = Collections.singletonList(this.backOperator); + } List customOperatorIds = opinion.getOperatorIds(); if (customOperatorIds != null && !customOperatorIds.isEmpty()) { - operators = operators.stream().filter(operator -> customOperatorIds.contains(operator.getUserId())).collect(Collectors.toList()); + operators = operators.stream() + .filter(operator -> customOperatorIds.contains(operator.getUserId())).collect(Collectors.toList()); if (operators.size() != customOperatorIds.size()) { throw new IllegalArgumentException("operator not match."); } diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/SignTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/SignTest.java index c4073ceb..6c47cf15 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/SignTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/SignTest.java @@ -124,6 +124,113 @@ void unSignTest(){ } + /** + * 多人非会签拒绝测试 + */ + @Test + void unSignRejectTest(){ + PageRequest pageRequest = PageRequest.of(0, 1000); + + User caocao = new User("曹操"); + userRepository.save(caocao); + User lvBu = new User("吕布"); + userRepository.save(lvBu); + User zhaoYun = new User("赵云"); + userRepository.save(zhaoYun); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, + OperatorMatcher.specifyOperatorMatcher(dept.getUserId(),caocao.getUserId(),lvBu.getUserId(),zhaoYun.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意").specify(lvBu.getUserId())); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(0, deptTodos.size()); + + // 查看部门刘备经理的待办 + List lvBuTodos = flowRecordRepository.findTodoByOperatorId(lvBu.getUserId(), pageRequest).getContent(); + assertEquals(1, lvBuTodos.size()); + + // 提交部门经理的审批 + FlowRecord lvBuTodo = lvBuTodos.get(0); + flowService.submitFlow(lvBuTodo.getId(), lvBu, leave, Opinion.pass("同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.reject("不同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent();; + assertEquals(4, records.size()); + + lvBuTodos = flowRecordRepository.findTodoByOperatorId(lvBu.getUserId(), pageRequest).getContent(); + assertEquals(1, lvBuTodos.size()); + + // 提交部门经理的审批 + lvBuTodo = lvBuTodos.get(0); + flowService.submitFlow(lvBuTodo.getId(), lvBu, leave, Opinion.pass("同意")); + + + bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("行吧")); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(0, userTodos.size()); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(5, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(6, snapshots.size()); + + } + /** * 多人会签测试 @@ -495,35 +602,6 @@ void signTrySubmitTest(){ List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); assertEquals(1, bossTodos.size()); -// // 查看部门经理 吕布 的待办 -// List lvbuTodos = flowRecordRepository.findTodoByOperatorId(lvBu.getUserId(), pageRequest).getContent(); -// assertEquals(1, lvbuTodos.size()); -// -// // 提交部门经理 吕布 的审批 -// FlowRecord lvbuTodo = lvbuTodos.get(0); -// flowService.submitFlow(lvbuTodo.getId(), lvBu, leave, Opinion.pass("吕布同意")); -// -// -// // 查看部门经理 赵云 的待办 -// List zhaoYunTodos = flowRecordRepository.findTodoByOperatorId(zhaoYun.getUserId(), pageRequest).getContent(); -// assertEquals(1, zhaoYunTodos.size()); -// -// // 提交部门经理 赵云 的审批 -// FlowRecord zhaoYunTodo = zhaoYunTodos.get(0); -// flowService.submitFlow(zhaoYunTodo.getId(), zhaoYun, leave, Opinion.pass("赵云同意")); -// -// -// // 查看部门经理 曹操 的待办 -// List caocaoTodos = flowRecordRepository.findTodoByOperatorId(caocao.getUserId(), pageRequest).getContent(); -// assertEquals(1, caocaoTodos.size()); -// -// // 提交部门经理 曹操 的审批 -// FlowRecord caocaoTodo = caocaoTodos.get(0); -// flowService.submitFlow(caocaoTodo.getId(), caocao, leave, Opinion.pass("曹操同意")); -// -// bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); -// assertEquals(1, bossTodos.size()); - // 提交总经理的审批 FlowRecord bossTodo = bossTodos.get(0); flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 0b2c8a84..8306aaa3 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.15 + 2.9.16 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index afc5c315..4e33f654 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.15 + 2.9.16 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 1c514784..3c735191 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.15 +CodingApi SpringBoot-Starter 2.9.16 springboot version (${spring-boot.version}) ------------------------------------------------------ From 4c663c3e5c73f81dbf88f21a9b8bb8c66c2089f1 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Thu, 2 Jan 2025 14:25:29 +0800 Subject: [PATCH 075/101] update 2.9.17 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../flow/build/FlowWorkBuilder.java | 5 + .../springboot/flow/domain/FlowWork.java | 8 + .../springboot/flow/pojo/FlowResult.java | 12 + .../serializable/FlowWorkSerializable.java | 8 + .../flow/service/FlowRecordVerifyService.java | 91 +++-- .../springboot/flow/service/FlowService.java | 33 +- .../service/FlowServiceRepositoryHolder.java | 29 ++ .../service/impl/FlowCustomEventService.java | 6 +- .../flow/service/impl/FlowDetailService.java | 3 +- .../service/impl/FlowPostponedService.java | 5 +- .../flow/service/impl/FlowRecallService.java | 7 +- .../flow/service/impl/FlowSaveService.java | 6 +- .../flow/service/impl/FlowStartService.java | 185 ++++++--- .../flow/service/impl/FlowSubmitService.java | 354 +++++++++++++----- .../service/impl/FlowTransferService.java | 5 +- .../service/impl/FlowTrySubmitService.java | 258 ++----------- .../flow/service/impl/FlowUrgeService.java | 7 +- .../springboot/flow/test/FlowTest.java | 77 ++++ springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 25 files changed, 672 insertions(+), 441 deletions(-) create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowServiceRepositoryHolder.java diff --git a/pom.xml b/pom.xml index 577ce5de..bfc27db6 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.16 + 2.9.17 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 68351e5c..39c26c89 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.16 + 2.9.17 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index aa4522a0..07a6998e 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.16 + 2.9.17 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index ecdd7f60..40335d3a 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.16 + 2.9.17 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/FlowWorkBuilder.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/FlowWorkBuilder.java index 2749b7cf..b9908f14 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/FlowWorkBuilder.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/FlowWorkBuilder.java @@ -41,6 +41,11 @@ public FlowWorkBuilder postponedMax(int postponedMax) { return this; } + public FlowWorkBuilder skipIfSameApprover(boolean skipIfSameApprover) { + this.work.setSkipIfSameApprover(skipIfSameApprover); + return this; + } + public FlowWorkBuilder title(String title) { this.work.setTitle(title); return this; diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowWork.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowWork.java index ffb91d08..b6ad791a 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowWork.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowWork.java @@ -60,6 +60,12 @@ public class FlowWork { */ private boolean enable; + /** + * 是否跳过相同审批人,默认为false + */ + @Setter + private boolean skipIfSameApprover; + /** * 最大延期次数 */ @@ -122,6 +128,7 @@ public FlowWork copy(){ flowWork.setTitle(this.getTitle()); flowWork.setCode(RandomGenerator.randomString(8)); flowWork.setPostponedMax(this.getPostponedMax()); + flowWork.setSkipIfSameApprover(this.isSkipIfSameApprover()); flowWork.schema(schema); return flowWork; } @@ -225,6 +232,7 @@ public FlowWorkSerializable toSerializable() { createUser.getUserId(), createTime, updateTime, + skipIfSameApprover, enable, postponedMax, schema, diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowResult.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowResult.java index 4e16e495..ad65b649 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowResult.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowResult.java @@ -2,10 +2,12 @@ import com.codingapi.springboot.flow.domain.FlowWork; import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.user.IFlowOperator; import lombok.Getter; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; @Getter public class FlowResult { @@ -23,4 +25,14 @@ public FlowResult(FlowWork flowWork,FlowRecord flowRecord) { this.records = new ArrayList<>(); this.records.add(flowRecord); } + + + /** + * 匹配操作者的记录 + * @param operator 操作者 + * @return 记录 + */ + public List matchRecordByOperator(IFlowOperator operator){ + return records.stream().filter(record -> record.isOperator(operator)).collect(Collectors.toList()); + } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowWorkSerializable.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowWorkSerializable.java index 1903d63d..d25d039a 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowWorkSerializable.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowWorkSerializable.java @@ -63,6 +63,13 @@ public class FlowWorkSerializable implements Serializable { * 更新时间 */ private long updateTime; + + /** + * 是否跳过相同审批人,默认为false + */ + @Setter + private boolean skipIfSameApprover; + /** * 是否启用 */ @@ -140,6 +147,7 @@ public FlowWork toFlowWork(FlowOperatorRepository flowOperatorRepository) { createTime, updateTime, enable, + skipIfSameApprover, postponedMax, flowNodes, relations.stream().map((item) -> item.toFlowRelation(flowNodes)).collect(Collectors.toList()), diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordVerifyService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordVerifyService.java index b923f10b..003db90c 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordVerifyService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowRecordVerifyService.java @@ -5,6 +5,7 @@ import com.codingapi.springboot.flow.record.FlowRecord; import com.codingapi.springboot.flow.repository.FlowProcessRepository; import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.repository.FlowWorkRepository; import com.codingapi.springboot.flow.user.IFlowOperator; import lombok.Getter; @@ -16,13 +17,13 @@ public class FlowRecordVerifyService { // constructor params - private final long recordId; @Getter private final IFlowOperator currentOperator; // register repository final FlowRecordRepository flowRecordRepository; final FlowProcessRepository flowProcessRepository; + final FlowWorkRepository flowWorkRepository; // load Object @Getter @@ -30,23 +31,44 @@ public class FlowRecordVerifyService { @Getter private FlowNode flowNode; @Getter - private FlowRecord flowRecord; + private final FlowRecord flowRecord; + + public FlowRecordVerifyService( + FlowWorkRepository flowWorkRepository, + FlowRecordRepository flowRecordRepository, + FlowProcessRepository flowProcessRepository, + long recordId, + IFlowOperator currentOperator) { + this.flowWorkRepository = flowWorkRepository; + this.flowRecordRepository = flowRecordRepository; + this.flowProcessRepository = flowProcessRepository; - public FlowRecordVerifyService(FlowRecordRepository flowRecordRepository, + this.currentOperator = currentOperator; + FlowRecord flowRecord = flowRecordRepository.getFlowRecordById(recordId); + if (flowRecord == null) { + throw new IllegalArgumentException("flow record not found"); + } + this.flowRecord = flowRecord; + } + + public FlowRecordVerifyService(FlowWorkRepository flowWorkRepository, + FlowRecordRepository flowRecordRepository, FlowProcessRepository flowProcessRepository, - long recordId, + FlowRecord flowRecord, + FlowWork flowWork, IFlowOperator currentOperator) { + this.flowWorkRepository = flowWorkRepository; this.flowRecordRepository = flowRecordRepository; this.flowProcessRepository = flowProcessRepository; this.currentOperator = currentOperator; - this.recordId = recordId; + this.flowRecord = flowRecord; + this.flowWork = flowWork; } - /** - * 校验流程记录是否已提交状态 + * 校验流程记录是否已提交状态 */ public void verifyFlowRecordSubmitState() { flowRecord.submitStateVerify(); @@ -56,13 +78,13 @@ public void verifyFlowRecordSubmitState() { * 校验流程是否当前操作者可操作的 */ public void verifyFlowRecordCurrentOperator() { - if(!currentOperator.isFlowManager()) { + if (!currentOperator.isFlowManager()) { flowRecord.matcherOperator(currentOperator); } } /** - * 校验流程是否已审批 + * 校验流程是否已审批 */ public void verifyFlowRecordNotDone() { if (flowRecord.isDone()) { @@ -72,7 +94,7 @@ public void verifyFlowRecordNotDone() { /** - * 校验流程是否已审批 + * 校验流程是否已审批 */ public void verifyFlowRecordIsDone() { if (!flowRecord.isDone()) { @@ -81,20 +103,19 @@ public void verifyFlowRecordIsDone() { } - /** - * 校验流程是否未审批 + * 校验流程是否未审批 */ public void verifyFlowRecordNotTodo() { if (flowRecord.isTodo()) { - if(!flowRecord.isStartRecord()) { + if (!flowRecord.isStartRecord()) { throw new IllegalArgumentException("flow record is todo"); } } } /** - * 校验流程是未审批 + * 校验流程是未审批 */ public void verifyFlowRecordIsTodo() { if (!flowRecord.isTodo()) { @@ -103,7 +124,7 @@ public void verifyFlowRecordIsTodo() { } /** - * 校验流程是否已完成 + * 校验流程是否已完成 */ public void verifyFlowRecordNotFinish() { if (flowRecord.isFinish()) { @@ -112,7 +133,7 @@ public void verifyFlowRecordNotFinish() { } /** - * 校验流程节点是否可编辑 + * 校验流程节点是否可编辑 */ public void verifyFlowNodeEditableState(boolean editable) { // 流程节点不可编辑时,不能保存 @@ -123,41 +144,35 @@ public void verifyFlowNodeEditableState(boolean editable) { /** - * 校验转办人员不能是当前操作者 + * 校验转办人员不能是当前操作者 */ public void verifyTargetOperatorIsNotCurrentOperator(IFlowOperator targetOperator) { - if(currentOperator.getUserId() == targetOperator.getUserId()){ + if (currentOperator.getUserId() == targetOperator.getUserId()) { throw new IllegalArgumentException("current operator is target operator"); } } /** - * 获取流程记录对象 - */ - public void loadFlowRecord() { - FlowRecord flowRecord = flowRecordRepository.getFlowRecordById(recordId); - if (flowRecord == null) { - throw new IllegalArgumentException("flow record not found"); - } - this.flowRecord = flowRecord; - } - - /** - * 获取流程设计对象 + * 获取流程设计对象 */ public void loadFlowWork() { - FlowWork flowWork = flowProcessRepository.getFlowWorkByProcessId(flowRecord.getProcessId()); - if (flowWork == null) { - throw new IllegalArgumentException("flow work not found"); + if (this.flowWork == null) { + FlowWork flowWork = flowProcessRepository.getFlowWorkByProcessId(flowRecord.getProcessId()); + if (flowWork == null) { + flowWork = flowWorkRepository.getFlowWorkByCode(flowRecord.getWorkCode()); + } + if (flowWork == null) { + throw new IllegalArgumentException("flow work not found"); + } + flowWork.enableValidate(); + this.flowWork = flowWork; } - flowWork.enableValidate(); - this.flowWork = flowWork; } /** - * 获取流程节点对象 + * 获取流程节点对象 */ public void loadFlowNode() { FlowNode flowNode = flowWork.getNodeByCode(flowRecord.getNodeCode()); @@ -168,11 +183,11 @@ public void loadFlowNode() { } /** - * 标记流程为已读状态 + * 标记流程为已读状态 */ public void setFlowRecordRead() { if (currentOperator != null) { - if(flowRecord.isOperator(currentOperator)) { + if (flowRecord.isOperator(currentOperator)) { if (!flowRecord.isRead()) { flowRecord.read(); flowRecordRepository.update(flowRecord); diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java index 2de3d976..a9aa8d13 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java @@ -20,16 +20,15 @@ public class FlowService { private final FlowDetailService flowDetailService; - private final FlowStartService flowStartService; - private final FlowSubmitService flowSubmitService; private final FlowCustomEventService flowCustomEventService; private final FlowRecallService flowRecallService; - private final FlowTrySubmitService flowTrySubmitService; private final FlowSaveService flowSaveService; private final FlowTransferService flowTransferService; private final FlowPostponedService flowPostponedService; private final FlowUrgeService flowUrgeService; + private final FlowServiceRepositoryHolder flowServiceRepositoryHolder; + public FlowService(FlowWorkRepository flowWorkRepository, FlowRecordRepository flowRecordRepository, @@ -37,16 +36,14 @@ public FlowService(FlowWorkRepository flowWorkRepository, FlowOperatorRepository flowOperatorRepository, FlowProcessRepository flowProcessRepository, FlowBackupRepository flowBackupRepository) { + this.flowServiceRepositoryHolder = new FlowServiceRepositoryHolder(flowWorkRepository, flowRecordRepository, flowBindDataRepository, flowOperatorRepository, flowProcessRepository, flowBackupRepository); this.flowDetailService = new FlowDetailService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, flowOperatorRepository, flowProcessRepository); - this.flowStartService = new FlowStartService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, flowOperatorRepository, flowProcessRepository, flowBackupRepository); - this.flowSubmitService = new FlowSubmitService(flowRecordRepository, flowBindDataRepository, flowOperatorRepository, flowProcessRepository); - this.flowCustomEventService = new FlowCustomEventService(flowRecordRepository, flowProcessRepository); - this.flowRecallService = new FlowRecallService(flowRecordRepository, flowProcessRepository); - this.flowTrySubmitService = new FlowTrySubmitService(flowRecordRepository, flowBindDataRepository, flowOperatorRepository, flowProcessRepository, flowWorkRepository, flowBackupRepository); - this.flowSaveService = new FlowSaveService(flowRecordRepository, flowBindDataRepository, flowProcessRepository); - this.flowTransferService = new FlowTransferService(flowRecordRepository, flowBindDataRepository, flowProcessRepository); - this.flowPostponedService = new FlowPostponedService(flowRecordRepository, flowProcessRepository); - this.flowUrgeService = new FlowUrgeService(flowRecordRepository, flowProcessRepository); + this.flowCustomEventService = new FlowCustomEventService(flowWorkRepository,flowRecordRepository, flowProcessRepository); + this.flowRecallService = new FlowRecallService(flowWorkRepository,flowRecordRepository, flowProcessRepository); + this.flowSaveService = new FlowSaveService(flowWorkRepository,flowRecordRepository, flowBindDataRepository, flowProcessRepository); + this.flowTransferService = new FlowTransferService(flowWorkRepository,flowRecordRepository, flowBindDataRepository, flowProcessRepository); + this.flowPostponedService = new FlowPostponedService(flowWorkRepository,flowRecordRepository, flowProcessRepository); + this.flowUrgeService = new FlowUrgeService(flowWorkRepository,flowRecordRepository, flowProcessRepository); } /** @@ -170,7 +167,8 @@ public void save(long recordId, IFlowOperator currentOperator, IBindData bindDat * @param advice 审批意见 */ public FlowResult startFlow(String workCode, IFlowOperator operator, IBindData bindData, String advice) { - return flowStartService.startFlow(workCode, operator, bindData, advice); + FlowStartService flowStartService = new FlowStartService(workCode, operator, bindData, advice, flowServiceRepositoryHolder); + return flowStartService.startFlow(); } @@ -183,7 +181,8 @@ public FlowResult startFlow(String workCode, IFlowOperator operator, IBindData b * @param opinion 审批意见 */ public FlowSubmitResult trySubmitFlow(long recordId, IFlowOperator currentOperator, IBindData bindData, Opinion opinion) { - return flowTrySubmitService.trySubmitFlow(recordId, currentOperator, bindData, opinion); + FlowTrySubmitService flowTrySubmitService = new FlowTrySubmitService(currentOperator, bindData, opinion, flowServiceRepositoryHolder); + return flowTrySubmitService.trySubmitFlow(recordId); } @@ -196,7 +195,8 @@ public FlowSubmitResult trySubmitFlow(long recordId, IFlowOperator currentOperat * @param opinion 审批意见 */ public FlowSubmitResult trySubmitFlow(String workCode, IFlowOperator currentOperator, IBindData bindData, Opinion opinion) { - return flowTrySubmitService.trySubmitFlow(workCode, currentOperator, bindData, opinion); + FlowTrySubmitService flowTrySubmitService = new FlowTrySubmitService(currentOperator, bindData, opinion, flowServiceRepositoryHolder); + return flowTrySubmitService.trySubmitFlow(workCode); } @@ -209,7 +209,8 @@ public FlowSubmitResult trySubmitFlow(String workCode, IFlowOperator currentOper * @param opinion 审批意见 */ public FlowResult submitFlow(long recordId, IFlowOperator currentOperator, IBindData bindData, Opinion opinion) { - return flowSubmitService.submitFlow(recordId, currentOperator, bindData, opinion); + FlowSubmitService flowSubmitService = new FlowSubmitService(recordId, currentOperator, bindData, opinion, flowServiceRepositoryHolder); + return flowSubmitService.submitFlow(); } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowServiceRepositoryHolder.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowServiceRepositoryHolder.java new file mode 100644 index 00000000..20b8c8b6 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowServiceRepositoryHolder.java @@ -0,0 +1,29 @@ +package com.codingapi.springboot.flow.service; + +import com.codingapi.springboot.flow.repository.*; +import lombok.Getter; + +@Getter +public class FlowServiceRepositoryHolder { + + private final FlowWorkRepository flowWorkRepository; + private final FlowRecordRepository flowRecordRepository; + private final FlowBindDataRepository flowBindDataRepository; + private final FlowOperatorRepository flowOperatorRepository; + private final FlowProcessRepository flowProcessRepository; + private final FlowBackupRepository flowBackupRepository; + + public FlowServiceRepositoryHolder(FlowWorkRepository flowWorkRepository, + FlowRecordRepository flowRecordRepository, + FlowBindDataRepository flowBindDataRepository, + FlowOperatorRepository flowOperatorRepository, + FlowProcessRepository flowProcessRepository, + FlowBackupRepository flowBackupRepository){ + this.flowWorkRepository = flowWorkRepository; + this.flowRecordRepository = flowRecordRepository; + this.flowBindDataRepository = flowBindDataRepository; + this.flowOperatorRepository = flowOperatorRepository; + this.flowProcessRepository = flowProcessRepository; + this.flowBackupRepository = flowBackupRepository; + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowCustomEventService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowCustomEventService.java index b7ab1dfc..67fbbbd8 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowCustomEventService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowCustomEventService.java @@ -8,6 +8,7 @@ import com.codingapi.springboot.flow.record.FlowRecord; import com.codingapi.springboot.flow.repository.FlowProcessRepository; import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.repository.FlowWorkRepository; import com.codingapi.springboot.flow.result.MessageResult; import com.codingapi.springboot.flow.service.FlowRecordVerifyService; import com.codingapi.springboot.flow.user.IFlowOperator; @@ -21,6 +22,7 @@ @AllArgsConstructor public class FlowCustomEventService { + private final FlowWorkRepository flowWorkRepository; private final FlowRecordRepository flowRecordRepository; private final FlowProcessRepository flowProcessRepository; @@ -34,10 +36,8 @@ public class FlowCustomEventService { * @param opinion 审批意见 */ public MessageResult customFlowEvent(long recordId, IFlowOperator currentOperator, String buttonId, IBindData bindData, Opinion opinion) { - FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, flowProcessRepository, recordId, currentOperator); + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowWorkRepository,flowRecordRepository, flowProcessRepository, recordId, currentOperator); - // 加载流程 - flowRecordVerifyService.loadFlowRecord(); // 验证流程的提交状态 flowRecordVerifyService.verifyFlowRecordSubmitState(); // 验证当前操作者 diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowDetailService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowDetailService.java index b2ea7409..e684f422 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowDetailService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowDetailService.java @@ -36,11 +36,10 @@ public class FlowDetailService { */ public FlowDetail detail(long recordId, IFlowOperator currentOperator) { - FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowWorkRepository,flowRecordRepository, flowProcessRepository, recordId, currentOperator); - flowRecordVerifyService.loadFlowRecord(); flowRecordVerifyService.setFlowRecordRead(); flowRecordVerifyService.loadFlowWork(); diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowPostponedService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowPostponedService.java index 6b11b0e5..68cbbc0f 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowPostponedService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowPostponedService.java @@ -4,6 +4,7 @@ import com.codingapi.springboot.flow.record.FlowRecord; import com.codingapi.springboot.flow.repository.FlowProcessRepository; import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.repository.FlowWorkRepository; import com.codingapi.springboot.flow.service.FlowRecordVerifyService; import com.codingapi.springboot.flow.user.IFlowOperator; import lombok.AllArgsConstructor; @@ -13,6 +14,7 @@ @AllArgsConstructor public class FlowPostponedService { + private final FlowWorkRepository flowWorkRepository; private final FlowRecordRepository flowRecordRepository; private final FlowProcessRepository flowProcessRepository; @@ -24,11 +26,10 @@ public class FlowPostponedService { * @param time 延期时间 */ public void postponed(long recordId, IFlowOperator currentOperator, long time) { - FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowWorkRepository,flowRecordRepository, flowProcessRepository, recordId, currentOperator); - flowRecordVerifyService.loadFlowRecord(); flowRecordVerifyService.verifyFlowRecordSubmitState(); flowRecordVerifyService.verifyFlowRecordCurrentOperator(); flowRecordVerifyService.loadFlowWork(); diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRecallService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRecallService.java index e0d98d0a..124f48b8 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRecallService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRecallService.java @@ -5,6 +5,7 @@ import com.codingapi.springboot.flow.record.FlowRecord; import com.codingapi.springboot.flow.repository.FlowProcessRepository; import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.repository.FlowWorkRepository; import com.codingapi.springboot.flow.service.FlowRecordVerifyService; import com.codingapi.springboot.flow.user.IFlowOperator; import com.codingapi.springboot.framework.event.EventPusher; @@ -18,6 +19,7 @@ @AllArgsConstructor public class FlowRecallService { + private final FlowWorkRepository flowWorkRepository; private final FlowRecordRepository flowRecordRepository; private final FlowProcessRepository flowProcessRepository; @@ -28,11 +30,12 @@ public class FlowRecallService { * @param currentOperator 当前操作者 */ public void recall(long recordId, IFlowOperator currentOperator) { - FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService( + flowWorkRepository, + flowRecordRepository, flowProcessRepository, recordId, currentOperator); - flowRecordVerifyService.loadFlowRecord(); flowRecordVerifyService.verifyFlowRecordCurrentOperator(); flowRecordVerifyService.loadFlowWork(); flowRecordVerifyService.loadFlowNode(); diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSaveService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSaveService.java index 174c18a3..6e57d924 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSaveService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSaveService.java @@ -7,6 +7,7 @@ import com.codingapi.springboot.flow.repository.FlowBindDataRepository; import com.codingapi.springboot.flow.repository.FlowProcessRepository; import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.repository.FlowWorkRepository; import com.codingapi.springboot.flow.service.FlowRecordVerifyService; import com.codingapi.springboot.flow.user.IFlowOperator; import lombok.AllArgsConstructor; @@ -16,6 +17,7 @@ @AllArgsConstructor public class FlowSaveService { + private final FlowWorkRepository flowWorkRepository; private final FlowRecordRepository flowRecordRepository; private final FlowBindDataRepository flowBindDataRepository; private final FlowProcessRepository flowProcessRepository; @@ -29,10 +31,10 @@ public class FlowSaveService { * @param advice 审批意见 */ public void save(long recordId, IFlowOperator currentOperator, IBindData bindData, String advice) { - FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowWorkRepository, + flowRecordRepository, flowProcessRepository, recordId, currentOperator); - flowRecordVerifyService.loadFlowRecord(); flowRecordVerifyService.verifyFlowRecordSubmitState(); flowRecordVerifyService.verifyFlowRecordCurrentOperator(); flowRecordVerifyService.loadFlowWork(); diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStartService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStartService.java index 80047a84..da90bf88 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStartService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStartService.java @@ -11,76 +11,100 @@ import com.codingapi.springboot.flow.record.FlowBackup; import com.codingapi.springboot.flow.record.FlowProcess; import com.codingapi.springboot.flow.record.FlowRecord; -import com.codingapi.springboot.flow.repository.*; +import com.codingapi.springboot.flow.repository.FlowBackupRepository; +import com.codingapi.springboot.flow.repository.FlowOperatorRepository; +import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.repository.FlowWorkRepository; import com.codingapi.springboot.flow.service.FlowNodeService; +import com.codingapi.springboot.flow.service.FlowServiceRepositoryHolder; import com.codingapi.springboot.flow.user.IFlowOperator; import com.codingapi.springboot.framework.event.EventPusher; -import lombok.AllArgsConstructor; +import lombok.Getter; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.List; @Transactional -@AllArgsConstructor public class FlowStartService { - private final FlowWorkRepository flowWorkRepository; - private final FlowRecordRepository flowRecordRepository; - private final FlowBindDataRepository flowBindDataRepository; - private final FlowOperatorRepository flowOperatorRepository; - private final FlowProcessRepository flowProcessRepository; - private final FlowBackupRepository flowBackupRepository; - + private final String workCode; + private final IFlowOperator operator; + private final IBindData bindData; + private final Opinion opinion; + private final FlowServiceRepositoryHolder flowServiceRepositoryHolder; + + + @Getter + private FlowWork flowWork; + private FlowNode flowNode; + private FlowBackup flowBackup; + private FlowProcess flowProcess; + private BindDataSnapshot snapshot; + private FlowNodeService flowNodeService; + + public FlowStartService(String workCode, + IFlowOperator operator, + IBindData bindData, + String advice, + FlowServiceRepositoryHolder flowServiceRepositoryHolder) { + this.workCode = workCode; + this.operator = operator; + this.bindData = bindData; + this.opinion = Opinion.pass(advice); + this.flowServiceRepositoryHolder = flowServiceRepositoryHolder; + } - /** - * 发起流程 (不自动提交到下一节点) - * - * @param workCode 流程编码 - * @param operator 操作者 - * @param bindData 绑定数据 - * @param advice 审批意见 - */ - public FlowResult startFlow(String workCode, IFlowOperator operator, IBindData bindData, String advice) { + private void loadFlowWork() { // 检测流程是否存在 - FlowWork flowWork = flowWorkRepository.getFlowWorkByCode(workCode); + FlowWorkRepository flowWorkRepository = flowServiceRepositoryHolder.getFlowWorkRepository(); + this.flowWork = flowWorkRepository.getFlowWorkByCode(workCode); if (flowWork == null) { throw new IllegalArgumentException("flow work not found"); } flowWork.verify(); flowWork.enableValidate(); + } - // 流程数据备份 - FlowBackup flowBackup = flowBackupRepository.getFlowBackupByWorkIdAndVersion(flowWork.getId(), flowWork.getUpdateTime()); + private void loadFlowBackup() { + FlowBackupRepository flowBackupRepository = flowServiceRepositoryHolder.getFlowBackupRepository(); + this.flowBackup = flowBackupRepository.getFlowBackupByWorkIdAndVersion(flowWork.getId(), flowWork.getUpdateTime()); if (flowBackup == null) { flowBackup = flowBackupRepository.backup(flowWork); } + } - // 保存流程 - FlowProcess flowProcess = new FlowProcess(flowBackup.getId(), operator); - flowProcessRepository.save(flowProcess); - - // 保存绑定数据 - BindDataSnapshot snapshot = new BindDataSnapshot(bindData); - flowBindDataRepository.save(snapshot); + private void saveFlowProcess() { + this.flowProcess = new FlowProcess(flowBackup.getId(), operator); + flowServiceRepositoryHolder.getFlowProcessRepository().save(flowProcess); + } - // 创建流程id - String processId = flowProcess.getProcessId(); + private void saveBindDataSnapshot() { + snapshot = new BindDataSnapshot(bindData); + flowServiceRepositoryHolder.getFlowBindDataRepository().save(snapshot); + } - // 构建审批意见 - Opinion opinion = Opinion.pass(advice); + private void buildFlowNodeService() { // 获取开始节点 FlowNode start = flowWork.getStartNode(); if (start == null) { throw new IllegalArgumentException("start node not found"); } + + this.flowNode = start; // 设置开始流程的上一个流程id long preId = 0; + // 创建流程id + String processId = flowProcess.getProcessId(); + List historyRecords = new ArrayList<>(); - FlowNodeService flowNodeService = new FlowNodeService(flowOperatorRepository, + FlowOperatorRepository flowOperatorRepository = flowServiceRepositoryHolder.getFlowOperatorRepository(); + FlowRecordRepository flowRecordRepository = flowServiceRepositoryHolder.getFlowRecordRepository(); + + flowNodeService = new FlowNodeService(flowOperatorRepository, flowRecordRepository, snapshot, opinion, @@ -93,6 +117,43 @@ public FlowResult startFlow(String workCode, IFlowOperator operator, IBindData b preId); flowNodeService.setNextNode(start); + } + + + private void pushEvent(int flowApprovalEventState, FlowRecord flowRecord) { + EventPusher.push(new FlowApprovalEvent(flowApprovalEventState, + flowRecord, + flowRecord.getCurrentOperator(), + flowWork, + snapshot.toBindData()), + true); + } + + + private void saveFlowRecords(List flowRecords) { + FlowRecordRepository flowRecordRepository = flowServiceRepositoryHolder.getFlowRecordRepository(); + flowRecordRepository.save(flowRecords); + } + + + /** + * 发起流程 (不自动提交到下一节点) + */ + public FlowResult startFlow() { + // 检测流程是否存在 + this.loadFlowWork(); + + // 流程数据备份 + this.loadFlowBackup(); + + // 保存流程 + this.saveFlowProcess(); + + // 保存绑定数据 + this.saveBindDataSnapshot(); + + // 构建流程节点服务 + this.buildFlowNodeService(); // 创建待办记录 List records = flowNodeService.createRecord(); @@ -111,32 +172,64 @@ public FlowResult startFlow(String workCode, IFlowOperator operator, IBindData b record.finish(); } - flowRecordRepository.save(records); + this.saveFlowRecords(records); // 推送事件 for (FlowRecord record : records) { - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_CREATE, record, operator, flowWork, snapshot.toBindData()), true); - - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_FINISH, - record, - operator, - flowWork, - snapshot.toBindData()), - true); + this.pushEvent(FlowApprovalEvent.STATE_CREATE, record); + this.pushEvent(FlowApprovalEvent.STATE_FINISH, record); } return new FlowResult(flowWork, records); } // 保存流程记录 - flowRecordRepository.save(records); + this.saveFlowRecords(records); // 推送事件消息 for (FlowRecord record : records) { - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_CREATE, record, operator, flowWork, snapshot.toBindData()), true); - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, record, operator, flowWork, snapshot.toBindData()), true); + this.pushEvent(FlowApprovalEvent.STATE_CREATE, record); + this.pushEvent(FlowApprovalEvent.STATE_TODO, record); } // 当前的审批记录 return new FlowResult(flowWork, records); } + + public FlowRecord tryStartFlow() { + // 检测流程是否存在 + this.loadFlowWork(); + // 流程数据备份 + this.loadFlowBackup(); + + // 保存绑定数据 + snapshot = new BindDataSnapshot(bindData); + // 保存流程 + flowProcess = new FlowProcess(flowBackup.getId(), operator); + + // 构建流程节点服务 + this.buildFlowNodeService(); + + FlowRecord startRecord = null; + + // 创建待办记录 + List records = flowNodeService.createRecord(); + if (records.isEmpty()) { + throw new IllegalArgumentException("flow record not found"); + } else { + for (FlowRecord record : records) { + record.updateOpinion(opinion); + startRecord = record; + } + } + + // 检测流程是否结束 + if (flowNodeService.nextNodeIsOver()) { + for (FlowRecord record : records) { + record.submitRecord(operator, snapshot, opinion, FlowSourceDirection.PASS); + record.finish(); + startRecord = record; + } + } + return startRecord; + } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java index 3f1b1136..610a22cd 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java @@ -8,17 +8,16 @@ import com.codingapi.springboot.flow.em.FlowSourceDirection; import com.codingapi.springboot.flow.event.FlowApprovalEvent; import com.codingapi.springboot.flow.pojo.FlowResult; +import com.codingapi.springboot.flow.pojo.FlowSubmitResult; import com.codingapi.springboot.flow.record.FlowRecord; import com.codingapi.springboot.flow.repository.FlowBindDataRepository; -import com.codingapi.springboot.flow.repository.FlowOperatorRepository; -import com.codingapi.springboot.flow.repository.FlowProcessRepository; import com.codingapi.springboot.flow.repository.FlowRecordRepository; import com.codingapi.springboot.flow.service.FlowDirectionService; import com.codingapi.springboot.flow.service.FlowNodeService; import com.codingapi.springboot.flow.service.FlowRecordVerifyService; +import com.codingapi.springboot.flow.service.FlowServiceRepositoryHolder; import com.codingapi.springboot.flow.user.IFlowOperator; import com.codingapi.springboot.framework.event.EventPusher; -import lombok.AllArgsConstructor; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; @@ -26,32 +25,74 @@ import java.util.stream.Collectors; @Transactional -@AllArgsConstructor public class FlowSubmitService { - private final FlowRecordRepository flowRecordRepository; - private final FlowBindDataRepository flowBindDataRepository; - private final FlowOperatorRepository flowOperatorRepository; - private final FlowProcessRepository flowProcessRepository; - /** - * 提交流程 - * - * @param recordId 流程记录id - * @param currentOperator 当前操作者 - * @param bindData 绑定数据 - * @param opinion 审批意见 - */ - public FlowResult submitFlow(long recordId, IFlowOperator currentOperator, IBindData bindData, Opinion opinion) { + private final IFlowOperator currentOperator; + private final IBindData bindData; + private final Opinion opinion; + private final FlowServiceRepositoryHolder flowServiceRepositoryHolder; + private final FlowRecordVerifyService flowRecordVerifyService; + - FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, flowProcessRepository, recordId, currentOperator); + private FlowRecord flowRecord; + private FlowWork flowWork; + private FlowNode flowNode; + private FlowNode nextNode; + + + private BindDataSnapshot snapshot; + private FlowNodeService flowNodeService; + private FlowDirectionService flowDirectionService; + private FlowSourceDirection flowSourceDirection; + + + public FlowSubmitService(long recordId, + IFlowOperator currentOperator, + IBindData bindData, + Opinion opinion, + FlowServiceRepositoryHolder flowServiceRepositoryHolder) { + this.flowServiceRepositoryHolder = flowServiceRepositoryHolder; + this.currentOperator = currentOperator; + this.bindData = bindData; + this.opinion = opinion; + this.flowRecordVerifyService = new FlowRecordVerifyService( + flowServiceRepositoryHolder.getFlowWorkRepository(), + flowServiceRepositoryHolder.getFlowRecordRepository(), + flowServiceRepositoryHolder.getFlowProcessRepository(), + recordId, + currentOperator); + } - // 加载流程 - flowRecordVerifyService.loadFlowRecord(); + + public FlowSubmitService(FlowRecord flowRecord, + FlowWork flowWork, + IFlowOperator currentOperator, + IBindData bindData, + Opinion opinion, + FlowServiceRepositoryHolder flowServiceRepositoryHolder) { + this.flowWork = flowWork; + this.flowServiceRepositoryHolder = flowServiceRepositoryHolder; + this.currentOperator = currentOperator; + this.bindData = bindData; + this.opinion = opinion; + this.flowRecordVerifyService = new FlowRecordVerifyService( + flowServiceRepositoryHolder.getFlowWorkRepository(), + flowServiceRepositoryHolder.getFlowRecordRepository(), + flowServiceRepositoryHolder.getFlowProcessRepository(), + flowRecord, + flowWork, + currentOperator); + } + + + // 加载流程 + private void loadFlow(boolean testSubmit) { // 验证流程的提交状态 flowRecordVerifyService.verifyFlowRecordSubmitState(); // 验证当前操作者 flowRecordVerifyService.verifyFlowRecordCurrentOperator(); + // 加载流程设计 flowRecordVerifyService.loadFlowWork(); // 加载流程节点 @@ -59,72 +100,84 @@ public FlowResult submitFlow(long recordId, IFlowOperator currentOperator, IBind // 验证没有子流程 flowRecordVerifyService.verifyChildrenRecordsIsEmpty(); - // 获取流程记录对象 - FlowRecord flowRecord = flowRecordVerifyService.getFlowRecord(); - FlowNode flowNode = flowRecordVerifyService.getFlowNode(); - FlowWork flowWork = flowRecordVerifyService.getFlowWork(); - + if (testSubmit) { + this.flowRecord = flowRecordVerifyService.getFlowRecord().copy(); + } else { + this.flowRecord = flowRecordVerifyService.getFlowRecord(); + } + this.flowNode = flowRecordVerifyService.getFlowNode(); + this.flowWork = flowRecordVerifyService.getFlowWork(); + } - // 保存流程表单快照数据 - BindDataSnapshot snapshot = null; + // 保存流程表单快照数据 + private void saveSnapshot(boolean testSubmit) { + FlowBindDataRepository flowBindDataRepository = flowServiceRepositoryHolder.getFlowBindDataRepository(); if (flowNode.isEditable()) { snapshot = new BindDataSnapshot(bindData); - flowBindDataRepository.save(snapshot); + if (!testSubmit) { + flowBindDataRepository.save(snapshot); + } } else { snapshot = flowBindDataRepository.getBindDataSnapshotById(flowRecord.getSnapshotId()); } + } + // 加载流程审批方向 + private void loadFlowDirection() { // 审批方向判断服务 - FlowDirectionService flowDirectionService = new FlowDirectionService(flowRecordVerifyService.getFlowNode(), flowRecordVerifyService.getFlowWork(), opinion); + flowDirectionService = new FlowDirectionService(flowRecordVerifyService.getFlowNode(), flowRecordVerifyService.getFlowWork(), opinion); // 加载流程审批方向 flowDirectionService.loadFlowSourceDirection(); // 验证审批方向 flowDirectionService.verifyFlowSourceDirection(); - // 根据当前方向提交流程 - FlowSourceDirection flowSourceDirection = flowDirectionService.getFlowSourceDirection(); - flowRecord.submitRecord(currentOperator, snapshot, opinion, flowSourceDirection); - flowRecordRepository.update(flowRecord); + flowSourceDirection = flowDirectionService.getFlowSourceDirection(); + } + + // 与当前流程同级的流程记录 + private List loadHistoryRecords(boolean testSubmit) { + FlowRecordRepository flowRecordRepository = flowServiceRepositoryHolder.getFlowRecordRepository(); // 与当前流程同级的流程记录 List historyRecords; if (flowRecord.isStartRecord()) { historyRecords = new ArrayList<>(); } else { - historyRecords = flowRecordRepository.findFlowRecordByPreId(flowRecord.getPreId()); - } - flowDirectionService.bindHistoryRecords(historyRecords); + if (testSubmit) { + // copy 流程数据防止影响原有数据 + historyRecords = flowRecordRepository.findFlowRecordByPreId(flowRecord.getPreId()).stream().map(FlowRecord::copy).collect(Collectors.toList()); + // 更新当前流程记录, 由于try测试过程中没有对数据落库,所以这里需要手动更新 + for (FlowRecord record : historyRecords) { + if (record.getId() == flowRecord.getId()) { + record.submitRecord(currentOperator, snapshot, opinion, flowSourceDirection); + } + } - // 判断流程是否结束(会签时需要所有人都通过) - if (flowNode.isSign()) { - boolean isDone = flowDirectionService.hasCurrentFlowNodeIsDone(); - if (!isDone) { - List todoRecords = historyRecords.stream().filter(FlowRecord::isTodo).collect(Collectors.toList()); - return new FlowResult(flowWork, todoRecords); + } else { + historyRecords = flowRecordRepository.findFlowRecordByPreId(flowRecord.getPreId()); } } + return historyRecords; + } - // 非会签下,当有人提交以后,将所有未提交的流程都自动提交,然后再执行下一节点 - if (flowNode.isUnSign()) { - for (FlowRecord record : historyRecords) { - if (record.isTodo() && record.getId() != flowRecord.getId()) { - record.autoPass(currentOperator, snapshot); - flowRecordRepository.update(flowRecord); - } - } - } - // 根据所有提交意见,重新加载审批方向 - flowSourceDirection = flowDirectionService.reloadFlowSourceDirection(); + // 保存流程记录 + private void saveFlowRecord(FlowRecord flowRecord) { + FlowRecordRepository flowRecordRepository = flowServiceRepositoryHolder.getFlowRecordRepository(); + flowRecordRepository.update(flowRecord); + } + + // 生成下一节点的流程记录 + private void loadNextNode(List historyRecords) { // 获取流程的发起者 IFlowOperator createOperator = flowRecord.getCreateOperator(); // 构建流程创建器 - FlowNodeService flowNodeService = new FlowNodeService( - flowOperatorRepository, - flowRecordRepository, + flowNodeService = new FlowNodeService( + flowServiceRepositoryHolder.getFlowOperatorRepository(), + flowServiceRepositoryHolder.getFlowRecordRepository(), snapshot, opinion, createOperator, @@ -146,51 +199,184 @@ public FlowResult submitFlow(long recordId, IFlowOperator currentOperator, IBind // 审批拒绝,并且自定了返回节点 flowNodeService.loadCustomBackNode(flowNode, flowRecord.getPreId()); } + this.nextNode = flowNodeService.getNextNode(); + } + + + // 更新流程记录 + private void updateFinishFlowRecord() { + flowServiceRepositoryHolder.getFlowRecordRepository().finishFlowRecordByProcessId(flowRecord.getProcessId()); + } + + // 保存流程记录 + private void saveFlowRecords(List flowRecords) { + flowServiceRepositoryHolder.getFlowRecordRepository().save(flowRecords); + } + + // 推送审批事件消息 + private void pushEvent(FlowRecord flowRecord, int eventState) { + EventPusher.push(new FlowApprovalEvent(eventState, + flowRecord, + flowRecord.getCurrentOperator(), + flowWork, + snapshot.toBindData() + ), true); + } + + + /** + * 提交流程 根据流程的是否跳过相同审批人来判断是否需要继续提交 + * + * @return 流程结果 + */ + public FlowResult submitFlow() { + FlowResult flowResult = this.submitCurrentFlow(); + if (this.isSkipIfSameApprover()) { + List flowRecords = flowResult.matchRecordByOperator(currentOperator); + FlowResult result = flowResult; + if (!flowRecords.isEmpty()) { + for (FlowRecord flowRecord : flowRecords) { + FlowSubmitService flowSubmitService = new FlowSubmitService(flowRecord.getId(), currentOperator, bindData, opinion, flowServiceRepositoryHolder); + result = flowSubmitService.submitCurrentFlow(); + } + } + return result; + } else { + return flowResult; + } + } + + /** + * 提交当前流程 + * + * @return 流程结果 + */ + private FlowResult submitCurrentFlow() { + // 加载流程信息 + this.loadFlow(false); + + // 保存流程表单快照数据 + this.saveSnapshot(false); + + // 审批方向判断服务 + this.loadFlowDirection(); + + // 提交流程记录 + flowRecord.submitRecord(currentOperator, snapshot, opinion, flowSourceDirection); + this.saveFlowRecord(flowRecord); + + // 与当前流程同级的流程记录 + List historyRecords = this.loadHistoryRecords(false); + flowDirectionService.bindHistoryRecords(historyRecords); + + // 判断流程是否结束(会签时需要所有人都通过) + if (flowNode.isSign()) { + boolean isDone = flowDirectionService.hasCurrentFlowNodeIsDone(); + if (!isDone) { + List todoRecords = historyRecords.stream().filter(FlowRecord::isTodo).collect(Collectors.toList()); + return new FlowResult(flowWork, todoRecords); + } + } - List records = flowNodeService.createRecord(); + // 非会签下,当有人提交以后,将所有未提交的流程都自动提交,然后再执行下一节点 + if (flowNode.isUnSign()) { + for (FlowRecord record : historyRecords) { + if (record.isTodo() && record.getId() != flowRecord.getId()) { + record.autoPass(currentOperator, snapshot); + FlowRecordRepository flowRecordRepository = flowServiceRepositoryHolder.getFlowRecordRepository(); + flowRecordRepository.update(flowRecord); + } + } + } + + // 根据所有提交意见,重新加载审批方向 + flowSourceDirection = flowDirectionService.reloadFlowSourceDirection(); + + this.loadNextNode(historyRecords); + + // 生成下一节点的流程记录 + List nextRecords = flowNodeService.createRecord(); // 判断流程是否完成 if (flowNodeService.nextNodeIsOver()) { flowRecord.submitRecord(currentOperator, snapshot, opinion, flowSourceDirection); flowRecord.finish(); - flowRecordRepository.update(flowRecord); - flowRecordRepository.finishFlowRecordByProcessId(flowRecord.getProcessId()); - - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_FINISH, - flowRecord, - currentOperator, - flowWork, - snapshot.toBindData()), - true); - if(!records.isEmpty()) { - return new FlowResult(flowWork, records.get(0)); + this.saveFlowRecord(flowRecord); + this.updateFinishFlowRecord(); + + this.pushEvent(flowRecord, FlowApprovalEvent.STATE_CREATE); + + if (!nextRecords.isEmpty()) { + return new FlowResult(flowWork, nextRecords.get(0)); } return new FlowResult(flowWork, flowRecord); } // 保存流程记录 - flowRecordRepository.save(records); + this.saveFlowRecords(nextRecords); // 推送审批事件消息 int eventState = flowSourceDirection == FlowSourceDirection.PASS ? FlowApprovalEvent.STATE_PASS : FlowApprovalEvent.STATE_REJECT; - EventPusher.push(new FlowApprovalEvent(eventState, - flowRecord, - currentOperator, - flowWork, - snapshot.toBindData()), - true); + this.pushEvent(flowRecord, eventState); // 推送待办事件消息 - for (FlowRecord record : records) { - IFlowOperator pushOperator = record.getCurrentOperator(); - EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, - record, - pushOperator, - flowWork, - snapshot.toBindData()), - true); + for (FlowRecord record : nextRecords) { + this.pushEvent(record, FlowApprovalEvent.STATE_TODO); } - return new FlowResult(flowWork, records); + return new FlowResult(flowWork, nextRecords); + } + + /** + * 提交流程 + **/ + public FlowSubmitResult trySubmitFlow() { + // 加载流程信息 + this.loadFlow(true); + + // 保存流程表单快照数据 + this.saveSnapshot(true); + + // 审批方向判断服务 + this.loadFlowDirection(); + + // 提交流程记录 + flowRecord.submitRecord(currentOperator, snapshot, opinion, flowSourceDirection); + + // 与当前流程同级的流程记录 + List historyRecords = this.loadHistoryRecords(true); + flowDirectionService.bindHistoryRecords(historyRecords); + + // 判断流程是否结束(会签时需要所有人都通过) + if (flowNode.isSign()) { + boolean isDone = flowDirectionService.hasCurrentFlowNodeIsDone(); + if (!isDone) { + List todoRecords = historyRecords.stream().filter(FlowRecord::isTodo).collect(Collectors.toList()); + return new FlowSubmitResult(flowWork, flowNode, todoRecords.stream().map(FlowRecord::getCurrentOperator).collect(Collectors.toList())); + } + } + + // 非会签下,当有人提交以后,将所有未提交的流程都自动提交,然后再执行下一节点 + if (flowNode.isUnSign()) { + for (FlowRecord record : historyRecords) { + if (record.isTodo() && record.getId() != flowRecord.getId()) { + record.autoPass(currentOperator, snapshot); + } + } + } + + // 根据所有提交意见,重新加载审批方向 + flowSourceDirection = flowDirectionService.reloadFlowSourceDirection(); + + this.loadNextNode(historyRecords); + + List operators = flowNodeService.loadNextNodeOperators(); + return new FlowSubmitResult(flowWork, nextNode, operators); + } + + + // 是否跳过相同审批人 + public boolean isSkipIfSameApprover() { + return flowWork.isSkipIfSameApprover() && !nextNode.isOverNode(); } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTransferService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTransferService.java index 429d79f7..fc6e8007 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTransferService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTransferService.java @@ -11,6 +11,7 @@ import com.codingapi.springboot.flow.repository.FlowBindDataRepository; import com.codingapi.springboot.flow.repository.FlowProcessRepository; import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.repository.FlowWorkRepository; import com.codingapi.springboot.flow.service.FlowRecordVerifyService; import com.codingapi.springboot.flow.user.IFlowOperator; import com.codingapi.springboot.framework.event.EventPusher; @@ -25,6 +26,7 @@ @AllArgsConstructor public class FlowTransferService { + private final FlowWorkRepository flowWorkRepository; private final FlowRecordRepository flowRecordRepository; private final FlowBindDataRepository flowBindDataRepository; private final FlowProcessRepository flowProcessRepository; @@ -41,11 +43,10 @@ public class FlowTransferService { */ public void transfer(long recordId, IFlowOperator currentOperator, IFlowOperator targetOperator, IBindData bindData, String advice) { - FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowWorkRepository, flowRecordRepository, flowProcessRepository, recordId, currentOperator); - flowRecordVerifyService.loadFlowRecord(); flowRecordVerifyService.verifyFlowRecordSubmitState(); flowRecordVerifyService.verifyFlowRecordCurrentOperator(); flowRecordVerifyService.verifyTargetOperatorIsNotCurrentOperator(targetOperator); diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTrySubmitService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTrySubmitService.java index d80ed9f4..a136f492 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTrySubmitService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowTrySubmitService.java @@ -1,256 +1,44 @@ package com.codingapi.springboot.flow.service.impl; -import com.codingapi.springboot.flow.bind.BindDataSnapshot; import com.codingapi.springboot.flow.bind.IBindData; -import com.codingapi.springboot.flow.domain.FlowNode; -import com.codingapi.springboot.flow.domain.FlowWork; import com.codingapi.springboot.flow.domain.Opinion; -import com.codingapi.springboot.flow.em.FlowSourceDirection; import com.codingapi.springboot.flow.pojo.FlowSubmitResult; -import com.codingapi.springboot.flow.record.FlowBackup; -import com.codingapi.springboot.flow.record.FlowProcess; import com.codingapi.springboot.flow.record.FlowRecord; -import com.codingapi.springboot.flow.repository.*; -import com.codingapi.springboot.flow.service.FlowDirectionService; -import com.codingapi.springboot.flow.service.FlowNodeService; -import com.codingapi.springboot.flow.service.FlowRecordVerifyService; +import com.codingapi.springboot.flow.service.FlowServiceRepositoryHolder; import com.codingapi.springboot.flow.user.IFlowOperator; -import lombok.AllArgsConstructor; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - @Transactional -@AllArgsConstructor public class FlowTrySubmitService { - private final FlowRecordRepository flowRecordRepository; - private final FlowBindDataRepository flowBindDataRepository; - private final FlowOperatorRepository flowOperatorRepository; - private final FlowProcessRepository flowProcessRepository; - private final FlowWorkRepository flowWorkRepository; - private final FlowBackupRepository flowBackupRepository; - - - /** - * 尝试提交流程 (流程过程中) - * - * @param recordId 流程记录id - * @param currentOperator 当前操作者 - * @param bindData 绑定数据 - * @param opinion 审批意见 - */ - public FlowSubmitResult trySubmitFlow(long recordId, IFlowOperator currentOperator, IBindData bindData, Opinion opinion) { - - FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, flowProcessRepository, recordId, currentOperator); - - // 加载流程 - flowRecordVerifyService.loadFlowRecord(); - // 验证流程的提交状态 - flowRecordVerifyService.verifyFlowRecordSubmitState(); - // 验证当前操作者 - flowRecordVerifyService.verifyFlowRecordCurrentOperator(); - // 加载流程设计 - flowRecordVerifyService.loadFlowWork(); - // 加载流程节点 - flowRecordVerifyService.loadFlowNode(); - // 验证没有子流程 - flowRecordVerifyService.verifyChildrenRecordsIsEmpty(); - - // 获取流程记录对象 copy 流程数据防止影响原有数据 - FlowRecord flowRecord = flowRecordVerifyService.getFlowRecord().copy(); - FlowNode flowNode = flowRecordVerifyService.getFlowNode(); - FlowWork flowWork = flowRecordVerifyService.getFlowWork(); - - return trySubmitFlow(flowWork, flowNode, flowRecord, currentOperator, bindData, opinion); + private final IFlowOperator currentOperator; + private final IBindData bindData; + private final Opinion opinion; + private final FlowServiceRepositoryHolder flowServiceRepositoryHolder; + + public FlowTrySubmitService(IFlowOperator currentOperator, + IBindData bindData, + Opinion opinion, + FlowServiceRepositoryHolder flowServiceRepositoryHolder) { + this.currentOperator = currentOperator; + this.bindData = bindData; + this.opinion = opinion; + this.flowServiceRepositoryHolder = flowServiceRepositoryHolder; } - /** - * 预提交流程数据查询 - * - * @param flowWork 流程设计 - * @param flowNode 流程节点 - * @param flowRecord 流程记录 - * @param currentOperator 当前操作者 - * @param bindData 绑定数据 - * @param opinion 审批意见 - * @return FlowSubmitResult - */ - private FlowSubmitResult trySubmitFlow(FlowWork flowWork, FlowNode flowNode, FlowRecord flowRecord, IFlowOperator currentOperator, IBindData bindData, Opinion opinion) { - - // 保存流程表单快照数据 - BindDataSnapshot snapshot = null; - if (flowNode.isEditable()) { - snapshot = new BindDataSnapshot(bindData); - } else { - snapshot = flowBindDataRepository.getBindDataSnapshotById(flowRecord.getSnapshotId()); - } - - // 审批方向判断服务 - FlowDirectionService flowDirectionService = new FlowDirectionService(flowNode, flowWork, opinion); - - // 加载流程审批方向 - flowDirectionService.loadFlowSourceDirection(); - // 验证审批方向 - flowDirectionService.verifyFlowSourceDirection(); - - // 根据当前方向提交流程 - FlowSourceDirection flowSourceDirection = flowDirectionService.getFlowSourceDirection(); - flowRecord.submitRecord(currentOperator, snapshot, opinion, flowSourceDirection); - - // 与当前流程同级的流程记录 - List historyRecords; - if (flowRecord.isStartRecord()) { - historyRecords = new ArrayList<>(); - } else { - // copy 流程数据防止影响原有数据 - historyRecords = flowRecordRepository.findFlowRecordByPreId(flowRecord.getPreId()).stream().map(FlowRecord::copy).collect(Collectors.toList()); - // 更新当前流程记录, 由于try测试过程中没有对数据落库,所以这里需要手动更新 - for(FlowRecord record : historyRecords){ - if(record.getId() == flowRecord.getId()){ - record.submitRecord(currentOperator, snapshot, opinion, flowSourceDirection); - } - } - } - flowDirectionService.bindHistoryRecords(historyRecords); - - // 判断流程是否结束(会签时需要所有人都通过) - if (flowNode.isSign()) { - boolean isDone = flowDirectionService.hasCurrentFlowNodeIsDone(); - if (!isDone) { - List todoRecords = historyRecords.stream().filter(FlowRecord::isTodo).collect(Collectors.toList()); - return new FlowSubmitResult(flowWork, flowNode, todoRecords.stream().map(FlowRecord::getCurrentOperator).collect(Collectors.toList())); - } - } - - // 非会签下,当有人提交以后,将所有未提交的流程都自动提交,然后再执行下一节点 - if (flowNode.isUnSign()) { - for (FlowRecord record : historyRecords) { - if (record.isTodo() && record.getId() != flowRecord.getId()) { - record.autoPass(currentOperator, snapshot); - } - } - } - - // 根据所有提交意见,重新加载审批方向 - flowDirectionService.reloadFlowSourceDirection(); - - // 获取流程的发起者 - IFlowOperator createOperator = flowRecord.getCreateOperator(); - - // 构建流程创建器 - FlowNodeService flowNodeService = new FlowNodeService( - flowOperatorRepository, - flowRecordRepository, - snapshot, - opinion, - createOperator, - currentOperator, - historyRecords, - flowWork, - flowRecord, - flowRecord.getProcessId(), - flowRecord.getId() - ); - - // 审批通过并进入下一节点 - if (flowDirectionService.isPassRecord()) { - flowNodeService.loadNextPassNode(flowNode); - // 审批拒绝返回上一节点 - } else if (flowDirectionService.isDefaultBackRecord()) { - flowNodeService.loadDefaultBackNode(flowRecord.getPreId()); - } else { - // 审批拒绝,并且自定了返回节点 - flowNodeService.loadCustomBackNode(flowNode, flowRecord.getPreId()); - } - - FlowNode nextNode = flowNodeService.getNextNode(); - - List operators = flowNodeService.loadNextNodeOperators(); - return new FlowSubmitResult(flowWork, nextNode, operators); + public FlowSubmitResult trySubmitFlow(long recordId) { + FlowSubmitService flowSubmitService = new FlowSubmitService(recordId, currentOperator, bindData, opinion, flowServiceRepositoryHolder); + return flowSubmitService.trySubmitFlow(); } - /** - * 尝试提交流程 (发起流程) - * - * @param workCode 流程编码 - * @param currentOperator 当前操作者 - * @param bindData 绑定数据 - * @param opinion 审批意见 - */ - public FlowSubmitResult trySubmitFlow(String workCode, IFlowOperator currentOperator, IBindData bindData, Opinion opinion) { - // 检测流程是否存在 - FlowWork flowWork = flowWorkRepository.getFlowWorkByCode(workCode); - if (flowWork == null) { - throw new IllegalArgumentException("flow work not found"); - } - flowWork.verify(); - flowWork.enableValidate(); - - // 流程数据备份 - FlowBackup flowBackup = flowBackupRepository.getFlowBackupByWorkIdAndVersion(flowWork.getId(), flowWork.getUpdateTime()); - if (flowBackup == null) { - flowBackup = flowBackupRepository.backup(flowWork); - } - - // 保存流程 - FlowProcess flowProcess = new FlowProcess(flowBackup.getId(), currentOperator); - - // 保存绑定数据 - BindDataSnapshot snapshot = new BindDataSnapshot(bindData); - - // 创建流程id - String processId = flowProcess.getProcessId(); + public FlowSubmitResult trySubmitFlow(String workCode) { + FlowStartService flowStartService = new FlowStartService(workCode, currentOperator, bindData, opinion.getAdvice(), flowServiceRepositoryHolder); + FlowRecord flowRecord = flowStartService.tryStartFlow(); - // 获取开始节点 - FlowNode start = flowWork.getStartNode(); - if (start == null) { - throw new IllegalArgumentException("start node not found"); - } - // 设置开始流程的上一个流程id - long preId = 0; - - List historyRecords = new ArrayList<>(); - - FlowNodeService flowNodeService = new FlowNodeService(flowOperatorRepository, - flowRecordRepository, - snapshot, - opinion, - currentOperator, - currentOperator, - historyRecords, - flowWork, - null, - processId, - preId); - - flowNodeService.setNextNode(start); - - FlowRecord startRecord = null; - - // 创建待办记录 - List records = flowNodeService.createRecord(); - if (records.isEmpty()) { - throw new IllegalArgumentException("flow record not found"); - } else { - for (FlowRecord record : records) { - record.updateOpinion(opinion); - startRecord = record; - } - } - - // 检测流程是否结束 - if (flowNodeService.nextNodeIsOver()) { - for (FlowRecord record : records) { - record.submitRecord(currentOperator, snapshot, opinion, FlowSourceDirection.PASS); - record.finish(); - startRecord = record; - } - } - return this.trySubmitFlow(flowWork, start, startRecord, currentOperator, bindData, opinion); + FlowSubmitService flowSubmitService = new FlowSubmitService(flowRecord, flowStartService.getFlowWork(), currentOperator, bindData, opinion, flowServiceRepositoryHolder); + return flowSubmitService.trySubmitFlow(); } + } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowUrgeService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowUrgeService.java index 2ebfbfb8..ba2acda2 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowUrgeService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowUrgeService.java @@ -5,6 +5,7 @@ import com.codingapi.springboot.flow.record.FlowRecord; import com.codingapi.springboot.flow.repository.FlowProcessRepository; import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.repository.FlowWorkRepository; import com.codingapi.springboot.flow.service.FlowRecordVerifyService; import com.codingapi.springboot.flow.user.IFlowOperator; import com.codingapi.springboot.framework.event.EventPusher; @@ -18,6 +19,7 @@ public class FlowUrgeService { + private final FlowWorkRepository flowWorkRepository; private final FlowRecordRepository flowRecordRepository; private final FlowProcessRepository flowProcessRepository; @@ -28,10 +30,11 @@ public class FlowUrgeService { * @param currentOperator 当前操作者 */ public void urge(long recordId, IFlowOperator currentOperator) { - FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService(flowRecordRepository, + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService( + flowWorkRepository, + flowRecordRepository, flowProcessRepository, recordId, currentOperator); - flowRecordVerifyService.loadFlowRecord(); flowRecordVerifyService.loadFlowWork(); flowRecordVerifyService.verifyFlowRecordIsDone(); diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java index 113facaa..9a8b88ec 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java @@ -132,6 +132,83 @@ void entrustTest() { + + /** + * 自己审批时直接通过 + */ + @Test + void sameUserFlow() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .skipIfSameApprover(true) + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + assertEquals(0, userTodo.getTimeoutTime()); + + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看刘备经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交委托dept部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(3, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(0, userTodos.size()); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(3, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(4, snapshots.size()); + + } + + /** * 同意再拒绝 */ diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 8306aaa3..bf37ce61 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.16 + 2.9.17 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 4e33f654..39b52e01 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.16 + 2.9.17 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 3c735191..e421e5bf 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.16 +CodingApi SpringBoot-Starter 2.9.17 springboot version (${spring-boot.version}) ------------------------------------------------------ From 5a75142d764ba9fd2650e85b44cb5cec2de21785 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Thu, 2 Jan 2025 22:39:12 +0800 Subject: [PATCH 076/101] update 2.9.18 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../codingapi/springboot/flow/matcher/OperatorMatcher.java | 4 ++++ springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- 7 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index bfc27db6..bc7d5bcf 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.17 + 2.9.18 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 39c26c89..9c5f9578 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.17 + 2.9.18 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 07a6998e..d68ea8c9 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.17 + 2.9.18 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 40335d3a..cd307f12 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.17 + 2.9.18 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/matcher/OperatorMatcher.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/matcher/OperatorMatcher.java index 9580251f..e383d076 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/matcher/OperatorMatcher.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/matcher/OperatorMatcher.java @@ -5,6 +5,7 @@ import lombok.Getter; import org.springframework.util.StringUtils; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -113,6 +114,9 @@ public static OperatorMatcher creatorOperatorMatcher() { */ public List matcher(FlowSession flowSession) { List values = (List) runtime.invokeMethod("run", flowSession); + if (values == null) { + return new ArrayList<>(); + } return values.stream().map(item -> { if (item instanceof Number) { return ((Number) item).longValue(); diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index bf37ce61..225ce9c4 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.17 + 2.9.18 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 39b52e01..c982fa64 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.17 + 2.9.18 springboot-starter From 2297e2eefde4fea261d1dd87c9be9de311760a81 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Fri, 3 Jan 2025 10:17:22 +0800 Subject: [PATCH 077/101] update 2.9.19 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../springboot/flow/content/FlowSession.java | 26 ++++++++++++++++++- .../flow/content/FlowSessionBeanProvider.java | 7 +++++ .../flow/query/FlowRecordQuery.java | 14 ++++++++++ springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 10 files changed, 53 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index bc7d5bcf..6d980a16 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.18 + 2.9.19 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 9c5f9578..54619d80 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.18 + 2.9.19 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index d68ea8c9..86714e32 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.18 + 2.9.19 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index cd307f12..c9259613 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.18 + 2.9.19 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java index 515d42d9..3050e218 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java @@ -4,10 +4,12 @@ import com.codingapi.springboot.flow.domain.FlowNode; import com.codingapi.springboot.flow.domain.FlowWork; import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.FlowSourceDirection; import com.codingapi.springboot.flow.error.NodeResult; import com.codingapi.springboot.flow.error.OperatorResult; import com.codingapi.springboot.flow.pojo.FlowResult; import com.codingapi.springboot.flow.pojo.FlowSubmitResult; +import com.codingapi.springboot.flow.query.FlowRecordQuery; import com.codingapi.springboot.flow.record.FlowRecord; import com.codingapi.springboot.flow.result.MessageResult; import com.codingapi.springboot.flow.service.FlowService; @@ -66,9 +68,13 @@ public Object getBean(String beanName) { } + public T getBean(Class clazz) { + return provider.getBean(clazz); + } + /** - * 获取审批意见 + * 获取审批意见 */ public String getAdvice() { if (opinion != null) { @@ -177,6 +183,24 @@ public MessageResult rejectFlow() { return MessageResult.create(result); } + /** + * 是否为驳回状态 + * + * @return 是否为驳回状态 + */ + public boolean isRejectState() { + long preId = flowRecord.getPreId(); + if (preId == 0) { + return false; + } + FlowRecordQuery flowRecordQuery = getBean(FlowRecordQuery.class); + FlowRecord preRecord = flowRecordQuery.getFlowRecordById(preId); + if (preRecord != null) { + return preRecord.getFlowSourceDirection() == FlowSourceDirection.REJECT; + } + return false; + } + /** * 预提交流程 diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSessionBeanProvider.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSessionBeanProvider.java index 58b9351d..06731b8c 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSessionBeanProvider.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSessionBeanProvider.java @@ -27,4 +27,11 @@ public Object getBean(String beanName) { return null; } + public T getBean(Class clazz) { + if (spring != null) { + return spring.getBean(clazz); + } + return null; + } + } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/query/FlowRecordQuery.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/query/FlowRecordQuery.java index 75a7a5cd..1c365cfc 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/query/FlowRecordQuery.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/query/FlowRecordQuery.java @@ -10,9 +10,23 @@ public interface FlowRecordQuery { + /** + * 根据ID获取流程记录 + * @param id 流程记录ID + * @return 流程记录 + */ + FlowRecord getFlowRecordById(long id); + + + /** + * 查询所有流程记录 + * @param pageRequest 分页参数 + * @return 流程记录 + */ Page findAll(PageRequest pageRequest); + /** * 查看个人的未读与待办数据 * diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 225ce9c4..14d9e840 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.18 + 2.9.19 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index c982fa64..b5a00b5a 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.18 + 2.9.19 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index e421e5bf..4656f428 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.17 +CodingApi SpringBoot-Starter 2.9.19 springboot version (${spring-boot.version}) ------------------------------------------------------ From 31a88d3bb5e3d31542a83a612634aaa3779b5f08 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Fri, 3 Jan 2025 12:02:50 +0800 Subject: [PATCH 078/101] update 2.9.20 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- .../authorization/interceptor/DefaultSQLInterceptor.java | 5 +++++ springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../springboot/flow/service/impl/FlowSubmitService.java | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- springboot-starter/src/main/resources/META-INF/banner.txt | 2 +- 9 files changed, 13 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 6d980a16..73deeb55 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.19 + 2.9.20 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 54619d80..cb2e5a7a 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.19 + 2.9.20 springboot-starter-data-authorization diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/DefaultSQLInterceptor.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/DefaultSQLInterceptor.java index 8d658633..47bb18bf 100644 --- a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/DefaultSQLInterceptor.java +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/DefaultSQLInterceptor.java @@ -21,6 +21,11 @@ public boolean beforeHandler(String sql) { @Override public void afterHandler(String sql, String newSql, SQLException exception) { + if(exception!=null){ + log.error("sql:{}",sql); + log.error("newSql:{}",newSql); + log.error(exception.getMessage(),exception); + } if (DataAuthorizationPropertyContext.getInstance().showSql()) { log.info("newSql:{}", newSql); } diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 86714e32..265505cd 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.19 + 2.9.20 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index c9259613..cdedcc96 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.19 + 2.9.20 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java index 610a22cd..c63970e1 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java @@ -304,7 +304,7 @@ private FlowResult submitCurrentFlow() { this.saveFlowRecord(flowRecord); this.updateFinishFlowRecord(); - this.pushEvent(flowRecord, FlowApprovalEvent.STATE_CREATE); + this.pushEvent(flowRecord, FlowApprovalEvent.STATE_FINISH); if (!nextRecords.isEmpty()) { return new FlowResult(flowWork, nextRecords.get(0)); diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 14d9e840..95efd861 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.19 + 2.9.20 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index b5a00b5a..ae099bac 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.19 + 2.9.20 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 4656f428..24160f8e 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.19 +CodingApi SpringBoot-Starter 2.9.20 springboot version (${spring-boot.version}) ------------------------------------------------------ From 2c09484544f7c268b34137e834e36697cd64cba1 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Fri, 3 Jan 2025 22:08:29 +0800 Subject: [PATCH 079/101] update 2.9.21 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../springboot/flow/content/FlowSession.java | 30 ++++++++++++++++--- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 8 files changed, 33 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index 73deeb55..66ed8121 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.20 + 2.9.21 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index cb2e5a7a..a27a5c21 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.20 + 2.9.21 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 265505cd..58d364d6 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.20 + 2.9.21 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index cdedcc96..cc0dfe6d 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.20 + 2.9.21 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java index 3050e218..7d8accd4 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java @@ -184,11 +184,13 @@ public MessageResult rejectFlow() { } /** - * 是否为驳回状态 - * - * @return 是否为驳回状态 + * 上级节点的状态是驳回状态 + * @return 上级节点的状态是驳回状态 */ - public boolean isRejectState() { + public boolean backStateIsReject() { + if (flowRecord == null) { + return false; + } long preId = flowRecord.getPreId(); if (preId == 0) { return false; @@ -201,6 +203,26 @@ public boolean isRejectState() { return false; } + /** + * 上级节点的状态是驳回状态 + * + * @see #backStateIsReject() + */ + @Deprecated + public boolean isRejectState() { + return this.backStateIsReject(); + } + + /** + * 当前节点的状态是驳回状态 + */ + public boolean currentStateIsReject() { + if (flowRecord != null) { + return flowRecord.getFlowSourceDirection() == FlowSourceDirection.REJECT; + } + return false; + } + /** * 预提交流程 diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 95efd861..41518c1b 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.20 + 2.9.21 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index ae099bac..417d26c0 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.20 + 2.9.21 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 24160f8e..481827bc 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.20 +CodingApi SpringBoot-Starter 2.9.21 springboot version (${spring-boot.version}) ------------------------------------------------------ From ab0d896eaae10c1475dd70b6c9447f813f654f6f Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Wed, 8 Jan 2025 16:47:23 +0800 Subject: [PATCH 080/101] update 2.9.22 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../codingapi/springboot/flow/service/FlowNodeService.java | 3 --- .../springboot/flow/service/impl/FlowSubmitService.java | 5 +++++ springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- springboot-starter/src/main/resources/META-INF/banner.txt | 2 +- 9 files changed, 12 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index 66ed8121..113c8bd3 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.21 + 2.9.22 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index a27a5c21..3e4cdb54 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.21 + 2.9.22 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 58d364d6..23678969 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.21 + 2.9.22 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index cc0dfe6d..6ddcf793 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.21 + 2.9.22 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java index 4d3a092f..970bbb8d 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java @@ -253,9 +253,6 @@ private List createNextRecord() { if (customOperatorIds != null && !customOperatorIds.isEmpty()) { operators = operators.stream() .filter(operator -> customOperatorIds.contains(operator.getUserId())).collect(Collectors.toList()); - if (operators.size() != customOperatorIds.size()) { - throw new IllegalArgumentException("operator not match."); - } } List recordList; if (operators.isEmpty()) { diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java index c63970e1..08cb15d7 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java @@ -370,6 +370,11 @@ public FlowSubmitResult trySubmitFlow() { this.loadNextNode(historyRecords); + while (nextNode.isCirculate()){ + flowNodeService.skipCirculate(); + this.nextNode = flowNodeService.getNextNode(); + } + List operators = flowNodeService.loadNextNodeOperators(); return new FlowSubmitResult(flowWork, nextNode, operators); } diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 41518c1b..2a17b230 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.21 + 2.9.22 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 417d26c0..e17fb23e 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.21 + 2.9.22 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 481827bc..0a5dbd7a 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.21 +CodingApi SpringBoot-Starter 2.9.22 springboot version (${spring-boot.version}) ------------------------------------------------------ From c9524cebb23b3218f5fcb2c502303e42a0c2b101 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Fri, 10 Jan 2025 15:59:55 +0800 Subject: [PATCH 081/101] update 2.9.23 data authorization support union all sql --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- .../enhancer/DataPermissionSQLEnhancer.java | 24 ++++++++++++----- .../enhancer/TableColumnAliasHolder.java | 19 ++++++++++--- .../analyzer/SelectSQLAnalyzerTest.java | 27 +++++++++++++++++++ springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 10 files changed, 68 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index 113c8bd3..528c2cb2 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.22 + 2.9.23 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 3e4cdb54..f57c2c0f 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.22 + 2.9.23 springboot-starter-data-authorization diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/DataPermissionSQLEnhancer.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/DataPermissionSQLEnhancer.java index 66a91a6b..3c39d992 100644 --- a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/DataPermissionSQLEnhancer.java +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/enhancer/DataPermissionSQLEnhancer.java @@ -8,12 +8,10 @@ import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.Statement; -import net.sf.jsqlparser.statement.select.FromItem; -import net.sf.jsqlparser.statement.select.Join; -import net.sf.jsqlparser.statement.select.PlainSelect; -import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.select.*; import java.sql.SQLException; +import java.util.List; /** * 数据权限 SQL 增强器 @@ -45,8 +43,7 @@ public String getNewSQL() throws SQLException { if (statement instanceof Select) { tableColumnAliasHolder.holderAlias(); Select select = (Select) statement; - PlainSelect plainSelect = select.getPlainSelect(); - this.enhanceDataPermissionInSelect(plainSelect); + this.deepMatch(select); return statement.toString(); } } catch (Exception e) { @@ -55,6 +52,21 @@ public String getNewSQL() throws SQLException { return sql; } + + private void deepMatch(Select select) throws Exception { + if (select instanceof PlainSelect) { + PlainSelect plainSelect = select.getPlainSelect(); + this.enhanceDataPermissionInSelect(plainSelect); + } + if (select instanceof SetOperationList) { + SetOperationList setOperationList = select.getSetOperationList(); + List selectList = setOperationList.getSelects(); + for (Select selectItem : selectList) { + this.deepSearch(selectItem); + } + } } diff --git a/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/analyzer/SelectSQLAnalyzerTest.java b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/analyzer/SelectSQLAnalyzerTest.java index ce2309f6..008062a5 100644 --- a/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/analyzer/SelectSQLAnalyzerTest.java +++ b/springboot-starter-data-authorization/src/test/java/com/codingapi/springboot/authorization/analyzer/SelectSQLAnalyzerTest.java @@ -195,4 +195,31 @@ void test7() throws Exception{ System.out.println(builder.getNewSQL()); System.out.println(builder.getTableAlias());; } + + + @Test + @Order(8) + void test8() throws Exception{ + String sql = "select ade1_0.id,ade1_0.ba_dept_name,ade1_0.ba_org_shortname,ade1_0.ba_dept_code,ade1_0.ba_code,ade1_0.ba_dept_property_code,ade1_0.ba_parent_type,ade1_0.ba_real_super_org_id,ade1_0.ba_org_is_avoidance_dept,ade1_0.ba_real_super_org_id,ade1_0.ba_super_org_name " + + "from ba04_administrative_department ade1_0 where ade1_0.ba_parent_type=0" + + " union all select ade2_0.id,ade2_0.ba_dept_name,ade2_0.ba_org_shortname,ade2_0.ba_dept_code,ade2_0.ba_code,ade2_0.ba_dept_property_code,ade2_0.ba_parent_type,ade2_0.ba_real_super_org_id,ade2_0.ba_org_is_avoidance_dept,ade3_0.ba_real_super_org_id,ade3_0.ba_super_org_name" + + " from ba04_administrative_department ade2_0 left join ba04_administrative_department ade3_0 on ade2_0.ba_real_super_org_id=ade3_0.id " + + "where ade2_0.ba_real_super_org_id=1"; + + + + RowHandler rowHandler = (subSql, tableName, tableAlias) -> { + if (tableName.equalsIgnoreCase("ba04_administrative_department")) { + String conditionTemplate = "%s.id < 100 "; + return Condition.formatCondition(conditionTemplate, tableAlias); + } + return null; + }; + DataPermissionSQLEnhancer builder = new DataPermissionSQLEnhancer(sql, rowHandler); + String newSQL = builder.getNewSQL(); + System.out.println(newSQL); + System.out.println(builder.getTableAlias()); + assertEquals("SELECT ade1_0.id, ade1_0.ba_dept_name, ade1_0.ba_org_shortname, ade1_0.ba_dept_code, ade1_0.ba_code, ade1_0.ba_dept_property_code, ade1_0.ba_parent_type, ade1_0.ba_real_super_org_id, ade1_0.ba_org_is_avoidance_dept, ade1_0.ba_real_super_org_id, ade1_0.ba_super_org_name FROM ba04_administrative_department ade1_0 WHERE ade1_0.id < 100 AND ade1_0.ba_parent_type = 0 " + + "UNION ALL SELECT ade2_0.id, ade2_0.ba_dept_name, ade2_0.ba_org_shortname, ade2_0.ba_dept_code, ade2_0.ba_code, ade2_0.ba_dept_property_code, ade2_0.ba_parent_type, ade2_0.ba_real_super_org_id, ade2_0.ba_org_is_avoidance_dept, ade3_0.ba_real_super_org_id, ade3_0.ba_super_org_name FROM ba04_administrative_department ade2_0 LEFT JOIN ba04_administrative_department ade3_0 ON ade2_0.ba_real_super_org_id = ade3_0.id WHERE ade3_0.id < 100 AND ade2_0.id < 100 AND ade2_0.ba_real_super_org_id = 1", newSQL); + } } diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 23678969..6e549ff0 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.22 + 2.9.23 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 6ddcf793..075ee22c 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.22 + 2.9.23 springboot-starter-flow diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 2a17b230..973d3a01 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.22 + 2.9.23 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index e17fb23e..36352276 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.22 + 2.9.23 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 0a5dbd7a..e8ad2dcd 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.22 +CodingApi SpringBoot-Starter 2.9.23 springboot version (${spring-boot.version}) ------------------------------------------------------ From 7d0b79e6c7817b0e48b27fd573fb8de62ed71e8a Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Mon, 20 Jan 2025 17:53:28 +0800 Subject: [PATCH 082/101] update 2.9.24 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../java/com/codingapi/springboot/flow/pojo/FlowResult.java | 6 ++++++ .../com/codingapi/springboot/flow/record/FlowRecord.java | 4 ++++ .../springboot/flow/service/impl/FlowSubmitService.java | 4 ++-- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- springboot-starter/src/main/resources/META-INF/banner.txt | 2 +- 10 files changed, 19 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 528c2cb2..419db6b2 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.23 + 2.9.24 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index f57c2c0f..147c2962 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.23 + 2.9.24 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 6e549ff0..9e2bee2b 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.23 + 2.9.24 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 075ee22c..1c87e1bb 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.23 + 2.9.24 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowResult.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowResult.java index ad65b649..2f2c4b8b 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowResult.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowResult.java @@ -35,4 +35,10 @@ public FlowResult(FlowWork flowWork,FlowRecord flowRecord) { public List matchRecordByOperator(IFlowOperator operator){ return records.stream().filter(record -> record.isOperator(operator)).collect(Collectors.toList()); } + + + public boolean isOver() { + return records.stream().allMatch(FlowRecord::isOverNode); + } + } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java index 81dbef11..58e79ecc 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java @@ -432,4 +432,8 @@ public boolean isPostponed() { public boolean isStartRecord() { return this.preId == 0; } + + public boolean isOverNode() { + return this.nodeCode.equals(FlowNode.CODE_OVER); + } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java index 08cb15d7..3b80261f 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java @@ -231,13 +231,13 @@ private void pushEvent(FlowRecord flowRecord, int eventState) { */ public FlowResult submitFlow() { FlowResult flowResult = this.submitCurrentFlow(); - if (this.isSkipIfSameApprover()) { + if (this.isSkipIfSameApprover() && !flowResult.isOver()) { List flowRecords = flowResult.matchRecordByOperator(currentOperator); FlowResult result = flowResult; if (!flowRecords.isEmpty()) { for (FlowRecord flowRecord : flowRecords) { FlowSubmitService flowSubmitService = new FlowSubmitService(flowRecord.getId(), currentOperator, bindData, opinion, flowServiceRepositoryHolder); - result = flowSubmitService.submitCurrentFlow(); + result = flowSubmitService.submitFlow(); } } return result; diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 973d3a01..7e5727ee 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.23 + 2.9.24 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 36352276..5642dddc 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.23 + 2.9.24 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index e8ad2dcd..aa3f2d44 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.23 +CodingApi SpringBoot-Starter 2.9.24 springboot version (${spring-boot.version}) ------------------------------------------------------ From ef2700212021a2bd6f9ae7867c75f8d6ac7f5e03 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Tue, 21 Jan 2025 11:46:33 +0800 Subject: [PATCH 083/101] update 2.9.25 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../springboot/flow/em/FlowButtonType.java | 2 + .../repository/FlowProcessRepository.java | 3 + .../flow/repository/FlowRecordRepository.java | 6 ++ .../springboot/flow/service/FlowService.java | 12 +++ .../flow/service/impl/FlowRemoveService.java | 58 +++++++++++++++ .../repository/FlowProcessRepositoryImpl.java | 5 +- .../repository/FlowRecordRepositoryImpl.java | 5 ++ .../springboot/flow/test/FlowTest.java | 74 +++++++++++++++++++ springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 15 files changed, 171 insertions(+), 8 deletions(-) create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRemoveService.java diff --git a/pom.xml b/pom.xml index 419db6b2..59321c51 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.24 + 2.9.25 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 147c2962..7a605c60 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.24 + 2.9.25 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 9e2bee2b..da743344 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.24 + 2.9.25 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 1c87e1bb..57fc064a 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.24 + 2.9.25 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowButtonType.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowButtonType.java index 0fc7c71e..550766f9 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowButtonType.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowButtonType.java @@ -26,4 +26,6 @@ public enum FlowButtonType { CUSTOM, // 前端 VIEW, + // 删除 + REMOVE, } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowProcessRepository.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowProcessRepository.java index 892141ea..acf8cfe1 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowProcessRepository.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowProcessRepository.java @@ -13,4 +13,7 @@ public interface FlowProcessRepository { FlowWork getFlowWorkByProcessId(String processId); + + void deleteByProcessId(String processId); + } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java index 6948da3c..292d40e5 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java @@ -71,4 +71,10 @@ public interface FlowRecordRepository { */ void delete(List childrenRecords); + /** + * 根据流程id删除流程记录 + * @param processId 流程id + */ + void deleteByProcessId(String processId); + } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java index a9aa8d13..e70ff123 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java @@ -22,6 +22,7 @@ public class FlowService { private final FlowDetailService flowDetailService; private final FlowCustomEventService flowCustomEventService; private final FlowRecallService flowRecallService; + private final FlowRemoveService flowRemoveService; private final FlowSaveService flowSaveService; private final FlowTransferService flowTransferService; private final FlowPostponedService flowPostponedService; @@ -40,6 +41,7 @@ public FlowService(FlowWorkRepository flowWorkRepository, this.flowDetailService = new FlowDetailService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, flowOperatorRepository, flowProcessRepository); this.flowCustomEventService = new FlowCustomEventService(flowWorkRepository,flowRecordRepository, flowProcessRepository); this.flowRecallService = new FlowRecallService(flowWorkRepository,flowRecordRepository, flowProcessRepository); + this.flowRemoveService = new FlowRemoveService(flowWorkRepository,flowRecordRepository, flowProcessRepository); this.flowSaveService = new FlowSaveService(flowWorkRepository,flowRecordRepository, flowBindDataRepository, flowProcessRepository); this.flowTransferService = new FlowTransferService(flowWorkRepository,flowRecordRepository, flowBindDataRepository, flowProcessRepository); this.flowPostponedService = new FlowPostponedService(flowWorkRepository,flowRecordRepository, flowProcessRepository); @@ -239,4 +241,14 @@ public void recall(long recordId, IFlowOperator currentOperator) { } + + /** + * 删除流程 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + */ + public void remove(long recordId, IFlowOperator currentOperator) { + flowRemoveService.remove(recordId, currentOperator); + } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRemoveService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRemoveService.java new file mode 100644 index 00000000..82c42198 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowRemoveService.java @@ -0,0 +1,58 @@ +package com.codingapi.springboot.flow.service.impl; + +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.event.FlowApprovalEvent; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.FlowProcessRepository; +import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.repository.FlowWorkRepository; +import com.codingapi.springboot.flow.service.FlowRecordVerifyService; +import com.codingapi.springboot.flow.user.IFlowOperator; +import com.codingapi.springboot.framework.event.EventPusher; +import lombok.AllArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collections; +import java.util.List; + +@Transactional +@AllArgsConstructor +public class FlowRemoveService { + + private final FlowWorkRepository flowWorkRepository; + private final FlowRecordRepository flowRecordRepository; + private final FlowProcessRepository flowProcessRepository; + + + /** + * 删除流程 + * + * @param recordId 流程记录id + * @param currentOperator 当前操作者 + */ + public void remove(long recordId, IFlowOperator currentOperator) { + FlowRecordVerifyService flowRecordVerifyService = new FlowRecordVerifyService( + flowWorkRepository, + flowRecordRepository, + flowProcessRepository, + recordId, currentOperator); + + + flowRecordVerifyService.verifyFlowRecordCurrentOperator(); + flowRecordVerifyService.loadFlowWork(); + flowRecordVerifyService.loadFlowNode(); + flowRecordVerifyService.verifyFlowRecordNotFinish(); + flowRecordVerifyService.verifyFlowRecordIsTodo(); + FlowNode flowNode = flowRecordVerifyService.getFlowNode(); + FlowRecord flowRecord = flowRecordVerifyService.getFlowRecord(); + + if(!flowNode.isStartNode()){ + throw new IllegalArgumentException("flow record not remove"); + } + + flowProcessRepository.deleteByProcessId(flowRecord.getProcessId()); + + flowRecordRepository.deleteByProcessId(flowRecord.getProcessId()); + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowProcessRepositoryImpl.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowProcessRepositoryImpl.java index 782f0bae..b128f599 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowProcessRepositoryImpl.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowProcessRepositoryImpl.java @@ -35,5 +35,8 @@ public FlowWork getFlowWorkByProcessId(String processId) { return flowBackup.resume(userRepository); } - + @Override + public void deleteByProcessId(String processId) { + cache.removeIf(flowProcess -> flowProcess.getProcessId().equals(processId)); + } } diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java index 02d88d4b..59f60d7b 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java @@ -161,4 +161,9 @@ public void finishFlowRecordByProcessId(String processId) { public void delete(List childrenRecords) { cache.removeAll(childrenRecords); } + + @Override + public void deleteByProcessId(String processId) { + cache.removeIf(record -> record.getProcessId().equals(processId)); + } } diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java index 9a8b88ec..9135b6ba 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java @@ -893,6 +893,80 @@ void recallTest1() { } + + /** + * 删除流程测试 + */ + @Test + void removeTest1() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User lorne = new User("lorne"); + userRepository.save(lorne); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId(),lorne.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + + FlowSubmitResult flowSubmitResult = flowService.trySubmitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + assertEquals(flowSubmitResult.getOperators().size(), 2); + assertTrue(flowSubmitResult.getOperators().stream().map(IFlowOperator::getUserId).collect(Collectors.toList()).contains(dept.getUserId())); + assertTrue(flowSubmitResult.getOperators().stream().map(IFlowOperator::getUserId).collect(Collectors.toList()).contains(lorne.getUserId())); + + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意").specify(dept.getUserId())); + + + // 撤销流程 + flowService.recall(userTodo.getId(), user); + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(1, records.size()); + + flowService.remove(userTodo.getId(), user); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(0, records.size()); + + } + + /** * 撤销流程测试 */ diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 7e5727ee..338f30fb 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.24 + 2.9.25 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 5642dddc..ec83d4aa 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.24 + 2.9.25 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index aa3f2d44..8b1b3b74 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.24 +CodingApi SpringBoot-Starter 2.9.25 springboot version (${spring-boot.version}) ------------------------------------------------------ From 26b650c6ac3b637f2c4266459ec9ddc8237060bb Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Tue, 21 Jan 2025 17:29:50 +0800 Subject: [PATCH 084/101] update 2.9.26 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../springboot/flow/pojo/FlowStepResult.java | 28 +++++++ .../springboot/flow/service/FlowService.java | 13 +++ .../flow/service/impl/FlowStepService.java | 83 +++++++++++++++++++ .../springboot/flow/test/CirculateTest.java | 5 ++ .../springboot/flow/test/FlowTest.java | 6 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 12 files changed, 141 insertions(+), 8 deletions(-) create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowStepResult.java create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStepService.java diff --git a/pom.xml b/pom.xml index 59321c51..24174851 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.25 + 2.9.26 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 7a605c60..f6c5a124 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.25 + 2.9.26 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index da743344..1d3ee0a9 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.25 + 2.9.26 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 57fc064a..4de98f22 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.25 + 2.9.26 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowStepResult.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowStepResult.java new file mode 100644 index 00000000..1ce8100b --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowStepResult.java @@ -0,0 +1,28 @@ +package com.codingapi.springboot.flow.pojo; + +import com.codingapi.springboot.flow.domain.FlowNode; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +public class FlowStepResult { + + private final List flowNodes; + + public FlowStepResult() { + this.flowNodes = new ArrayList<>(); + } + + public void addFlowNode(FlowNode flowNode) { + this.flowNodes.add(flowNode); + } + + + public void print(){ + for (FlowNode flowNode : flowNodes) { + System.out.println("flowNode = " + flowNode.getName()); + } + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java index e70ff123..838e26a4 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java @@ -4,6 +4,7 @@ import com.codingapi.springboot.flow.domain.Opinion; import com.codingapi.springboot.flow.pojo.FlowDetail; import com.codingapi.springboot.flow.pojo.FlowResult; +import com.codingapi.springboot.flow.pojo.FlowStepResult; import com.codingapi.springboot.flow.pojo.FlowSubmitResult; import com.codingapi.springboot.flow.repository.*; import com.codingapi.springboot.flow.result.MessageResult; @@ -188,6 +189,18 @@ public FlowSubmitResult trySubmitFlow(long recordId, IFlowOperator currentOperat } + /** + * 获取流程执行节点 + * + * @param workCode + * @param currentOperator + * @return + */ + public FlowStepResult getFlowStep(String workCode, IBindData bindData, IFlowOperator currentOperator) { + FlowStepService flowStepService = new FlowStepService(workCode, currentOperator, bindData, flowServiceRepositoryHolder); + return flowStepService.getFlowStep(); + } + /** * 尝试提交流程 (发起流程) * diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStepService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStepService.java new file mode 100644 index 00000000..5da4bba1 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStepService.java @@ -0,0 +1,83 @@ +package com.codingapi.springboot.flow.service.impl; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.bind.IBindData; +import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.pojo.FlowStepResult; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.FlowOperatorRepository; +import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.service.FlowNodeService; +import com.codingapi.springboot.flow.service.FlowServiceRepositoryHolder; +import com.codingapi.springboot.flow.user.IFlowOperator; + +import java.util.ArrayList; +import java.util.List; + +public class FlowStepService { + private final FlowWork flowWork; + + private final IFlowOperator currentOperator; + private final IBindData bindData; + private final FlowServiceRepositoryHolder flowServiceRepositoryHolder; + + private FlowNodeService flowNodeService; + private FlowNode flowNode; + + public FlowStepService(String workCode, IFlowOperator currentOperator, IBindData bindData, FlowServiceRepositoryHolder flowServiceRepositoryHolder) { + this.currentOperator = currentOperator; + this.bindData = bindData; + this.flowServiceRepositoryHolder = flowServiceRepositoryHolder; + this.flowWork = flowServiceRepositoryHolder.getFlowWorkRepository().getFlowWorkByCode(workCode); + } + + + public FlowStepResult getFlowStep() { + FlowStepResult flowStepResult = new FlowStepResult(); + // 获取开始节点 + FlowNode start = flowWork.getStartNode(); + if (start == null) { + throw new IllegalArgumentException("start node not found"); + } + + this.flowNode = start; + // 设置开始流程的上一个流程id + long preId = 0; + + // 创建流程id + String processId = "flow_" + System.currentTimeMillis(); + + List historyRecords = new ArrayList<>(); + + FlowOperatorRepository flowOperatorRepository = flowServiceRepositoryHolder.getFlowOperatorRepository(); + FlowRecordRepository flowRecordRepository = flowServiceRepositoryHolder.getFlowRecordRepository(); + + BindDataSnapshot snapshot = new BindDataSnapshot(bindData); + flowNodeService = new FlowNodeService(flowOperatorRepository, + flowRecordRepository, + snapshot, + Opinion.pass("同意"), + currentOperator, + currentOperator, + historyRecords, + flowWork, + null, + processId, + preId); + + flowNodeService.setNextNode(start); + + this.flowNode = start; + flowStepResult.addFlowNode(this.flowNode); + + do { + flowNodeService.loadNextPassNode(this.flowNode); + this.flowNode = flowNodeService.getNextNode(); + flowStepResult.addFlowNode(this.flowNode); + } while (!flowNode.isOverNode()); + + return flowStepResult; + } +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/CirculateTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/CirculateTest.java index 7e198d49..1a1c72cc 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/CirculateTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/CirculateTest.java @@ -8,6 +8,7 @@ import com.codingapi.springboot.flow.flow.Leave; import com.codingapi.springboot.flow.matcher.OperatorMatcher; import com.codingapi.springboot.flow.pojo.FlowDetail; +import com.codingapi.springboot.flow.pojo.FlowStepResult; import com.codingapi.springboot.flow.record.FlowRecord; import com.codingapi.springboot.flow.repository.*; import com.codingapi.springboot.flow.service.FlowService; @@ -79,6 +80,10 @@ void circulate(){ // 创建流程 flowService.startFlow(workCode, user, leave, "发起流程"); + + FlowStepResult result = flowService.getFlowStep(workCode, leave, user); + result.print(); + // 查看我的待办 List userTodos = flowRecordRepository.findUnReadByOperatorId(user.getUserId(), pageRequest).getContent(); assertEquals(1, userTodos.size()); diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java index 9135b6ba..b2f90c6e 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java @@ -8,6 +8,7 @@ import com.codingapi.springboot.flow.flow.Leave; import com.codingapi.springboot.flow.matcher.OperatorMatcher; import com.codingapi.springboot.flow.pojo.FlowDetail; +import com.codingapi.springboot.flow.pojo.FlowStepResult; import com.codingapi.springboot.flow.pojo.FlowSubmitResult; import com.codingapi.springboot.flow.record.FlowRecord; import com.codingapi.springboot.flow.repository.*; @@ -72,6 +73,9 @@ void entrustTest() { Leave leave = new Leave("我要出去看看"); leaveRepository.save(leave); + FlowStepResult result = flowService.getFlowStep(workCode, leave, user); + result.print(); + // 创建流程 flowService.startFlow(workCode, user, leave, "发起流程"); @@ -898,7 +902,7 @@ void recallTest1() { * 删除流程测试 */ @Test - void removeTest1() { + void removeTest() { PageRequest pageRequest = PageRequest.of(0, 1000); User lorne = new User("lorne"); diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 338f30fb..3226f4b8 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.25 + 2.9.26 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index ec83d4aa..ff2152f6 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.25 + 2.9.26 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 8b1b3b74..c29a01a4 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.25 +CodingApi SpringBoot-Starter 2.9.26 springboot version (${spring-boot.version}) ------------------------------------------------------ From b89eddea183bcf37cbb3b9adf1856b67c487e846 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Wed, 22 Jan 2025 11:31:52 +0800 Subject: [PATCH 085/101] update 2.9.27 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../springboot/flow/pojo/FlowStepResult.java | 28 ++++++++++++++++--- .../flow/service/impl/FlowStepService.java | 4 +-- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 9 files changed, 33 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index 24174851..aae2e4a0 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.26 + 2.9.27 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index f6c5a124..6bbb7eb2 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.26 + 2.9.27 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 1d3ee0a9..549d6bbb 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.26 + 2.9.27 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 4de98f22..e971e4da 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.26 + 2.9.27 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowStepResult.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowStepResult.java index 1ce8100b..36d49c36 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowStepResult.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowStepResult.java @@ -1,6 +1,8 @@ package com.codingapi.springboot.flow.pojo; import com.codingapi.springboot.flow.domain.FlowNode; +import com.codingapi.springboot.flow.em.NodeType; +import com.codingapi.springboot.flow.user.IFlowOperator; import lombok.Getter; import java.util.ArrayList; @@ -9,20 +11,38 @@ @Getter public class FlowStepResult { - private final List flowNodes; + private final List flowNodes; public FlowStepResult() { this.flowNodes = new ArrayList<>(); } - public void addFlowNode(FlowNode flowNode) { - this.flowNodes.add(flowNode); + public void addFlowNode(FlowNode flowNode,List operators) { + this.flowNodes.add(new FlowStepNode(flowNode.getId(), flowNode.getCode(),flowNode.getName(),flowNode.getType(),operators)); } public void print(){ - for (FlowNode flowNode : flowNodes) { + for (FlowStepNode flowNode : flowNodes) { System.out.println("flowNode = " + flowNode.getName()); } } + + + @Getter + public static class FlowStepNode{ + private final String id; + private final String code; + private final String name; + private final NodeType type; + private final List operators; + + public FlowStepNode(String id, String code, String name, NodeType type,List operators) { + this.id = id; + this.code = code; + this.name = name; + this.type = type; + this.operators = operators; + } + } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStepService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStepService.java index 5da4bba1..c03833dd 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStepService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStepService.java @@ -70,12 +70,12 @@ public FlowStepResult getFlowStep() { flowNodeService.setNextNode(start); this.flowNode = start; - flowStepResult.addFlowNode(this.flowNode); + flowStepResult.addFlowNode(this.flowNode, this.flowNodeService.loadNextNodeOperators()); do { flowNodeService.loadNextPassNode(this.flowNode); this.flowNode = flowNodeService.getNextNode(); - flowStepResult.addFlowNode(this.flowNode); + flowStepResult.addFlowNode(this.flowNode, this.flowNodeService.loadNextNodeOperators()); } while (!flowNode.isOverNode()); return flowStepResult; diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 3226f4b8..f0a652d9 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.26 + 2.9.27 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index ff2152f6..faedd760 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.26 + 2.9.27 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index c29a01a4..8fc040be 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.26 +CodingApi SpringBoot-Starter 2.9.27 springboot version (${spring-boot.version}) ------------------------------------------------------ From a50655d8918ae9102a3e64a6d130efc76e751ee8 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Thu, 6 Feb 2025 10:19:25 +0800 Subject: [PATCH 086/101] update 2.9.28 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../springboot/flow/domain/FlowNode.java | 5 +- .../springboot/flow/domain/Opinion.java | 11 +++ .../springboot/flow/em/FlowType.java | 6 +- .../springboot/flow/record/FlowRecord.java | 7 ++ .../flow/service/FlowDirectionService.java | 3 +- .../flow/service/FlowNodeService.java | 6 +- .../springboot/flow/service/FlowService.java | 9 +++ .../flow/service/impl/FlowNotifyService.java | 81 +++++++++++++++++++ .../flow/service/impl/FlowSubmitService.java | 8 +- .../springboot/flow/test/FlowTest2.java | 70 +++++++++++++++- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 17 files changed, 201 insertions(+), 19 deletions(-) create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowNotifyService.java diff --git a/pom.xml b/pom.xml index aae2e4a0..baf5281c 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.27 + 2.9.28 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 6bbb7eb2..005e64de 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.27 + 2.9.28 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 549d6bbb..69b87a9b 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.27 + 2.9.28 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index e971e4da..9fee697b 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.27 + 2.9.28 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java index 37bc95ec..1e9985fb 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java @@ -217,7 +217,8 @@ public FlowRecord createRecord(long workId, String title, IFlowOperator createOperator, IFlowOperator currentOperator, - BindDataSnapshot snapshot) { + BindDataSnapshot snapshot, + boolean isWaiting) { // 当前操作者存在委托人时,才需要寻找委托人 IFlowOperator flowOperator = currentOperator; @@ -239,7 +240,7 @@ public FlowRecord createRecord(long workId, record.setPreId(preId); record.setTitle(title); record.setTimeoutTime(this.loadTimeoutTime()); - record.setFlowType(FlowType.TODO); + record.setFlowType(isWaiting?FlowType.WAITING:FlowType.TODO); record.setErrMessage(null); record.setSnapshotId(snapshot.getId()); return record; diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/Opinion.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/Opinion.java index 2c1dc8f0..f9f72e78 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/Opinion.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/Opinion.java @@ -33,6 +33,9 @@ public class Opinion { public static final int RESULT_REJECT = 3; // 审批结果 抄送 public static final int RESULT_CIRCULATE = 4; + // 审批结果 等待 + public static final int RESULT_WAITING = 5; + /** * 审批意见 @@ -88,6 +91,10 @@ public static Opinion transfer(String advice) { return new Opinion(advice, RESULT_TRANSFER, TYPE_DEFAULT); } + public static Opinion waiting(String advice) { + return new Opinion(advice, RESULT_WAITING, TYPE_DEFAULT); + } + public static Opinion unSignAutoSuccess() { return new Opinion("非会签自动审批", RESULT_PASS, TYPE_AUTO); } @@ -104,6 +111,10 @@ public boolean isSuccess() { return result == RESULT_PASS; } + public boolean isWaiting() { + return result == RESULT_WAITING; + } + public boolean isReject() { return result == RESULT_REJECT; } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowType.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowType.java index 79b54d00..5e36e57b 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowType.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/em/FlowType.java @@ -20,7 +20,11 @@ public enum FlowType { /** * 转办 */ - TRANSFER; + TRANSFER, + /** + * 等待执行 + */ + WAITING; public static FlowType parser(String type){ diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java index 58e79ecc..8c1aef89 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java @@ -321,6 +321,13 @@ public boolean isFinish() { return this.flowStatus == FlowStatus.FINISH; } + /** + * 是否等待 + */ + public boolean isWaiting() { + return this.flowType == FlowType.WAITING; + } + /** * 是否是待办 */ diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java index 829238bb..fb925dff 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java @@ -40,7 +40,7 @@ public void bindHistoryRecords(List historyRecords) { * 解析当前的审批方向 */ public void loadFlowSourceDirection() { - if (opinion.isSuccess()) { + if (opinion.isSuccess() || opinion.isWaiting()) { flowSourceDirection = FlowSourceDirection.PASS; } if (opinion.isReject()) { @@ -48,7 +48,6 @@ public void loadFlowSourceDirection() { } } - /** * 重新加载审批方向 * 根据会签结果判断是否需要重新设置审批方向 diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java index 970bbb8d..75bdf395 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java @@ -264,7 +264,7 @@ private List createNextRecord() { String recordTitle = nextNode.generateTitle(flowSession); recordList = new ArrayList<>(); for (IFlowOperator operator : operators) { - FlowRecord record = nextNode.createRecord(workId, flowWork.getCode(), processId, preId, recordTitle, createOperator, operator, snapshot); + FlowRecord record = nextNode.createRecord(workId, flowWork.getCode(), processId, preId, recordTitle, createOperator, operator, snapshot, opinion.isWaiting()); recordList.add(record); } } @@ -294,7 +294,7 @@ private List errMatcher(FlowNode currentNode, IFlowOperator currentO for (IFlowOperator operator : operators) { FlowSession content = new FlowSession(flowRecord, flowWork, currentNode, createOperator, operator, snapshot.toBindData(), opinion, historyRecords); String recordTitle = currentNode.generateTitle(content); - FlowRecord record = currentNode.createRecord(flowWork.getId(), flowWork.getCode(), processId, preId, recordTitle, createOperator, operator, snapshot); + FlowRecord record = currentNode.createRecord(flowWork.getId(), flowWork.getCode(), processId, preId, recordTitle, createOperator, operator, snapshot, opinion.isWaiting()); recordList.add(record); } return recordList; @@ -312,7 +312,7 @@ private List errMatcher(FlowNode currentNode, IFlowOperator currentO if (!matcherOperators.isEmpty()) { for (IFlowOperator matcherOperator : matcherOperators) { String recordTitle = node.generateTitle(content); - FlowRecord record = node.createRecord(flowWork.getId(), flowWork.getCode(), processId, preId, recordTitle, createOperator, matcherOperator, snapshot); + FlowRecord record = node.createRecord(flowWork.getId(), flowWork.getCode(), processId, preId, recordTitle, createOperator, matcherOperator, snapshot, opinion.isWaiting()); recordList.add(record); } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java index 838e26a4..0aaa054c 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowService.java @@ -228,6 +228,15 @@ public FlowResult submitFlow(long recordId, IFlowOperator currentOperator, IBind return flowSubmitService.submitFlow(); } + /** + * 唤醒流程 + * @param processId 流程实例id + * @param currentOperator 当前操作者 + */ + public void notifyFlow(String processId,IFlowOperator currentOperator) { + FlowNotifyService flowNotifyService = new FlowNotifyService(processId, currentOperator, flowServiceRepositoryHolder); + flowNotifyService.notifyFlow(); + } /** * 自定义事件 diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowNotifyService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowNotifyService.java new file mode 100644 index 00000000..53d11750 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowNotifyService.java @@ -0,0 +1,81 @@ +package com.codingapi.springboot.flow.service.impl; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.em.FlowType; +import com.codingapi.springboot.flow.event.FlowApprovalEvent; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.FlowBindDataRepository; +import com.codingapi.springboot.flow.repository.FlowProcessRepository; +import com.codingapi.springboot.flow.repository.FlowRecordRepository; +import com.codingapi.springboot.flow.repository.FlowWorkRepository; +import com.codingapi.springboot.flow.service.FlowServiceRepositoryHolder; +import com.codingapi.springboot.flow.user.IFlowOperator; +import com.codingapi.springboot.framework.event.EventPusher; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@Transactional +public class FlowNotifyService { + + private final String processId; + private final IFlowOperator currentOperator; + private final FlowRecordRepository flowRecordRepository; + private final FlowBindDataRepository flowBindDataRepository; + private final FlowWorkRepository flowWorkRepository; + private final FlowProcessRepository flowProcessRepository; + + + public FlowNotifyService(String processId, IFlowOperator currentOperator, FlowServiceRepositoryHolder flowServiceRepositoryHolder) { + this.processId = processId; + this.currentOperator = currentOperator; + this.flowRecordRepository = flowServiceRepositoryHolder.getFlowRecordRepository(); + this.flowBindDataRepository = flowServiceRepositoryHolder.getFlowBindDataRepository(); + this.flowWorkRepository = flowServiceRepositoryHolder.getFlowWorkRepository(); + this.flowProcessRepository = flowServiceRepositoryHolder.getFlowProcessRepository(); + } + + + /** + * 获取流程设计对象 + */ + public FlowWork loadFlowWork(FlowRecord flowRecord) { + FlowWork flowWork = flowProcessRepository.getFlowWorkByProcessId(flowRecord.getProcessId()); + if (flowWork == null) { + flowWork = flowWorkRepository.getFlowWorkByCode(flowRecord.getWorkCode()); + } + if (flowWork == null) { + throw new IllegalArgumentException("flow work not found"); + } + flowWork.enableValidate(); + + return flowWork; + } + + /** + * 流程通知 + */ + public void notifyFlow() { + List flowRecords = flowRecordRepository.findFlowRecordByProcessId(processId); + List waitingRecords = flowRecords.stream().filter(FlowRecord::isWaiting).collect(Collectors.toList()); + for (FlowRecord flowRecord : waitingRecords) { + if (flowRecord.isOperator(currentOperator)) { + flowRecord.setFlowType(FlowType.TODO); + flowRecordRepository.update(flowRecord); + + BindDataSnapshot snapshot = flowBindDataRepository.getBindDataSnapshotById(flowRecord.getSnapshotId()); + + FlowWork flowWork = this.loadFlowWork(flowRecord); + + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_TODO, + flowRecord, + flowRecord.getCurrentOperator(), + flowWork, + snapshot.toBindData() + ), true); + } + } + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java index 3b80261f..4f58d0d0 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java @@ -209,7 +209,7 @@ private void updateFinishFlowRecord() { } // 保存流程记录 - private void saveFlowRecords(List flowRecords) { + private void saveNextFlowRecords(List flowRecords) { flowServiceRepositoryHolder.getFlowRecordRepository().save(flowRecords); } @@ -313,7 +313,7 @@ private FlowResult submitCurrentFlow() { } // 保存流程记录 - this.saveFlowRecords(nextRecords); + this.saveNextFlowRecords(nextRecords); // 推送审批事件消息 int eventState = flowSourceDirection == FlowSourceDirection.PASS ? FlowApprovalEvent.STATE_PASS : FlowApprovalEvent.STATE_REJECT; @@ -321,7 +321,9 @@ private FlowResult submitCurrentFlow() { // 推送待办事件消息 for (FlowRecord record : nextRecords) { - this.pushEvent(record, FlowApprovalEvent.STATE_TODO); + if(record.isTodo()) { + this.pushEvent(record, FlowApprovalEvent.STATE_TODO); + } } return new FlowResult(flowWork, nextRecords); diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java index 7321f63c..a420e03a 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java @@ -30,7 +30,7 @@ public class FlowTest2 { private final FlowService flowService = new FlowService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, userRepository,flowProcessRepository,flowBackupRepository); /** - * flow test + * 转办测试 */ @Test void flowTest() { @@ -107,4 +107,72 @@ void flowTest() { assertTrue(records.stream().allMatch(FlowRecord::isFinish)); } + + + /** + * 流程等待测试 + */ + @Test + void flowWaitingTest() { + PageRequest pageRequest = PageRequest.of(0, 1000); + User lorne = new User("lorne"); + userRepository.save(lorne); + + User boss = new User("boss"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(lorne) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("老板审批", "boss", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .relations() + .relation("老板审批", "start", "boss") + .relation("结束节点", "boss", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我想要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, lorne, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + String processId = userTodos.get(0).getProcessId(); + + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), lorne, leave, Opinion.waiting("自己先提交")); + + // 查看boss的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(0, bossTodos.size()); + + // 通知流程 + flowService.notifyFlow(processId,boss); + + // 查看boss的待办 + bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("领导审批通过")); + + bossTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent(); + assertEquals(0, bossTodos.size()); + + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(2, records.size()); + + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + } } diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index f0a652d9..934dd494 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.27 + 2.9.28 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index faedd760..4393952f 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.27 + 2.9.28 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 8fc040be..3ca43830 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.27 +CodingApi SpringBoot-Starter 2.9.28 springboot version (${spring-boot.version}) ------------------------------------------------------ From 9ede2520482ff60eeb8f6eb2d7bf7c585c1a43bf Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Mon, 10 Feb 2025 09:39:21 +0800 Subject: [PATCH 087/101] update 2.9.29 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../springboot/flow/content/FlowSession.java | 4 +- .../springboot/flow/record/FlowRecord.java | 8 ++ .../flow/service/FlowNodeService.java | 54 ++++++--- .../flow/service/impl/FlowSubmitService.java | 2 +- .../springboot/flow/test/FlowTest2.java | 113 ++++++++++++++++++ springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 12 files changed, 168 insertions(+), 27 deletions(-) diff --git a/pom.xml b/pom.xml index baf5281c..bc9da7e4 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.28 + 2.9.29 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 005e64de..4e627f67 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.28 + 2.9.29 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 69b87a9b..acb0a010 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.28 + 2.9.29 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 9fee697b..9a1528d2 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.28 + 2.9.29 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java index 7d8accd4..60f94ae5 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/content/FlowSession.java @@ -24,7 +24,7 @@ @Getter public class FlowSession { - // 当前的流程记录 + // 当前的流程记录(当前审批的流程) private final FlowRecord flowRecord; // 当前的流程设计器 private final FlowWork flowWork; @@ -32,7 +32,7 @@ public class FlowSession { private final FlowNode flowNode; // 流程的创建者 private final IFlowOperator createOperator; - // 当前的操作者 + // 当前的操作者(当前的操作者,非代办人) private final IFlowOperator currentOperator; // 流程绑定数据 private final IBindData bindData; diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java index 8c1aef89..95b1a39b 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java @@ -344,6 +344,14 @@ public boolean isTransfer() { return this.flowType == FlowType.TRANSFER; } + + /** + * 拒绝状态 + */ + public boolean isReject() { + return this.opinion != null && this.opinion.isReject() && isDone(); + } + /** * 审批通过 */ diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java index 75bdf395..ba755514 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java @@ -101,24 +101,44 @@ public void skipCirculate() { /** * 加载默认回退节点 */ - public void loadDefaultBackNode(long parentRecordId) { - IFlowOperator flowOperator; - // 拒绝时,默认返回上一个已办节点 - FlowRecord preRecord = flowRecordRepository.getFlowRecordById(parentRecordId); - // 只寻找已办节点 - while (!preRecord.isDone()) { - // 继续寻找上一个节点 - preRecord = flowRecordRepository.getFlowRecordById(preRecord.getPreId()); - } - // 获取上一个节点的审批者,继续将审批者设置为当前审批者 - flowOperator = preRecord.getCurrentOperator(); - FlowNode nextNode = flowWork.getNodeByCode(preRecord.getNodeCode()); - if (nextNode == null) { - throw new IllegalArgumentException("next node not found"); + public void loadDefaultBackNode(FlowRecord currentRecord) { + List historyRecords = + flowRecordRepository.findFlowRecordByProcessId(currentRecord.getProcessId()) + .stream() + .sorted((o1, o2) -> (int) (o2.getId() - o1.getId())) + .filter(record -> record.getId() < currentRecord.getId()) + .collect(Collectors.toList()); + + int index = 0; + while (true) { + if (index >= historyRecords.size()) { + throw new IllegalArgumentException("back node not found"); + } + FlowRecord record = historyRecords.get(index); + if (record.isDone()) { + // 是连续的回退节点时,则根据流程记录的状态来判断 + if(record.isReject()){ + boolean startRemove = false; + for(FlowRecord historyRecord: historyRecords){ + if(startRemove){ + this.nextNode = flowWork.getNodeByCode(historyRecord.getNodeCode()); + this.nextOperator = historyRecord.getCurrentOperator(); + this.backOperator = historyRecord.getCurrentOperator(); + return; + } + if(historyRecord.getNodeCode().equals(currentRecord.getNodeCode())){ + startRemove = true; + } + } + }else { + this.nextNode = flowWork.getNodeByCode(record.getNodeCode()); + this.nextOperator = record.getCurrentOperator(); + this.backOperator = record.getCurrentOperator(); + return; + } + } + index++; } - this.nextNode = nextNode; - this.nextOperator = flowOperator; - this.backOperator = flowOperator; } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java index 4f58d0d0..5b01e3b3 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java @@ -194,7 +194,7 @@ private void loadNextNode(List historyRecords) { flowNodeService.loadNextPassNode(flowNode); // 审批拒绝返回上一节点 } else if (flowDirectionService.isDefaultBackRecord()) { - flowNodeService.loadDefaultBackNode(flowRecord.getPreId()); + flowNodeService.loadDefaultBackNode(flowRecord); } else { // 审批拒绝,并且自定了返回节点 flowNodeService.loadCustomBackNode(flowNode, flowRecord.getPreId()); diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java index a420e03a..f4f350a1 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java @@ -7,6 +7,7 @@ import com.codingapi.springboot.flow.em.ApprovalType; import com.codingapi.springboot.flow.flow.Leave; import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.pojo.FlowDetail; import com.codingapi.springboot.flow.record.FlowRecord; import com.codingapi.springboot.flow.repository.*; import com.codingapi.springboot.flow.service.FlowService; @@ -175,4 +176,116 @@ void flowWaitingTest() { assertTrue(records.stream().allMatch(FlowRecord::isFinish)); } + + + /** + * 部门拒绝再提交测试 + */ + @Test + void rejectTest() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + + // 查看流程详情 + FlowDetail flowDetail = flowService.detail(userTodo.getId()); + assertEquals("我要出去看看", ((Leave) flowDetail.getBindData()).getTitle()); + assertTrue(flowDetail.getFlowRecord().isUnRead()); + + + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + // 查看老板审批 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 老板审批不通过 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.reject("不同意")); + + // 部门经理查看到流程 + deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.reject("不同意")); + + // 查看用户的待办 + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 用户再次提交 + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 部门经理查看到流程 + deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.pass("同意")); + + // 查看老板审批 + bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 老板审批通过 + bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("同意")); + + + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(7, records.size()); + + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + } } diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 934dd494..d546706f 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.28 + 2.9.29 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 4393952f..4b498635 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.28 + 2.9.29 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 3ca43830..cc3a14af 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.28 +CodingApi SpringBoot-Starter 2.9.29 springboot version (${spring-boot.version}) ------------------------------------------------------ From 48120b08a9b673c3e2a11e8ab90960f5ca306045 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sat, 22 Feb 2025 16:09:39 +0800 Subject: [PATCH 088/101] update 2.9.30 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- .../springboot/fast/jpa/SQLBuilder.java | 16 ++++++++++++++++ springboot-starter-flow/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 8 files changed, 23 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index bc9da7e4..8332e528 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.29 + 2.9.30 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 4e627f67..9b772721 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.29 + 2.9.30 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index acb0a010..36ad9e09 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.29 + 2.9.30 4.0.0 diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/SQLBuilder.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/SQLBuilder.java index 0eaaf2e4..020e2802 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/SQLBuilder.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/SQLBuilder.java @@ -9,6 +9,7 @@ public class SQLBuilder { private final StringBuilder sqlBuilder; private final StringBuilder countSQLBuilder; + @Getter private int index; private final List params; @Getter @@ -39,6 +40,21 @@ public void append(String sql, Object value) { } } + public void addParam(Object value){ + params.add(value); + index++; + } + + public void addParam(Object value,int index){ + params.add(value); + this.index = index; + } + + public void appendSql(String sql){ + sqlBuilder.append(" ").append(sql).append(" "); + countSQLBuilder.append(" ").append(sql).append(" "); + } + public String getSQL() { return sqlBuilder.toString(); } diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 9a1528d2..197a1baf 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.29 + 2.9.30 springboot-starter-flow diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index d546706f..ccc8dcef 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.29 + 2.9.30 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 4b498635..9298754f 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.29 + 2.9.30 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index cc3a14af..70fefe7b 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.29 +CodingApi SpringBoot-Starter 2.9.30 springboot version (${spring-boot.version}) ------------------------------------------------------ From 78e5872c14defec9f416d93de062f1e8b86f52f4 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Mon, 24 Feb 2025 19:30:09 +0800 Subject: [PATCH 089/101] update 2.9.31 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../springboot/flow/service/impl/FlowSubmitService.java | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- springboot-starter/src/main/resources/META-INF/banner.txt | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 8332e528..c15d5c31 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.30 + 2.9.31 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 9b772721..4fb46f7e 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.30 + 2.9.31 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 36ad9e09..47f3e5ba 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.30 + 2.9.31 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 197a1baf..fe460060 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.30 + 2.9.31 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java index 5b01e3b3..69833b79 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java @@ -284,7 +284,7 @@ private FlowResult submitCurrentFlow() { if (record.isTodo() && record.getId() != flowRecord.getId()) { record.autoPass(currentOperator, snapshot); FlowRecordRepository flowRecordRepository = flowServiceRepositoryHolder.getFlowRecordRepository(); - flowRecordRepository.update(flowRecord); + flowRecordRepository.update(record); } } } diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index ccc8dcef..2ec3a47e 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.30 + 2.9.31 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 9298754f..ccd75aab 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.30 + 2.9.31 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 70fefe7b..92e6a74e 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.30 +CodingApi SpringBoot-Starter 2.9.31 springboot version (${spring-boot.version}) ------------------------------------------------------ From 53b8d7758ff572912623c36d242372efb7e890fd Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Wed, 12 Mar 2025 17:48:57 +0800 Subject: [PATCH 090/101] support search isNull isNotNull notIn --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- .../jpa/repository/DynamicSQLBuilder.java | 15 +++++ .../springboot/fast/DemoRepositoryTest.java | 63 ++++++++++++++++++- springboot-starter-flow/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../framework/dto/request/Filter.java | 12 ++++ .../framework/dto/request/Relation.java | 3 + .../src/main/resources/META-INF/banner.txt | 2 +- 11 files changed, 98 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index c15d5c31..ef24153e 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.31 + 2.9.32 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 4fb46f7e..bc5b0971 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.31 + 2.9.32 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 47f3e5ba..44d89bce 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.31 + 2.9.32 4.0.0 diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java index 68052924..50dc3b5b 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/repository/DynamicSQLBuilder.java @@ -99,6 +99,14 @@ private void buildSQL(Filter filter, StringBuilder hql) { paramIndex++; } + if (filter.isNull()) { + hql.append(filter.getKey()).append(" IS NULL "); + } + + if (filter.isNotNull()) { + hql.append(filter.getKey()).append(" IS NOT NULL "); + } + if (filter.isNotEqual()) { hql.append(filter.getKey()).append(" != ?").append(paramIndex); params.add(filter.getValue()[0]); @@ -125,6 +133,13 @@ private void buildSQL(Filter filter, StringBuilder hql) { params.add(Arrays.asList(filter.getValue())); paramIndex++; } + + if (filter.isNotIn()) { + hql.append(filter.getKey()).append(" NOT IN (").append("?").append(paramIndex).append(")"); + params.add(Arrays.asList(filter.getValue())); + paramIndex++; + } + if (filter.isGreaterThan()) { hql.append(filter.getKey()).append(" > ?").append(paramIndex); params.add(filter.getValue()[0]); diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java index 236f1d51..09ca9cdc 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java @@ -56,6 +56,44 @@ void findAll() { assertEquals(1, page.getTotalElements()); } + @Test + void pageRequestIsNull() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demo1 = demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demoRepository.save(demo2); + + PageRequest request = new PageRequest(); + request.setCurrent(1); + request.setPageSize(10); + request.addFilter("name", Relation.IS_NULL); + + Page page = demoRepository.pageRequest(request); + assertEquals(1, page.getTotalElements()); + } + + @Test + void pageRequestIsNotNull() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demo1 = demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demoRepository.save(demo2); + + PageRequest request = new PageRequest(); + request.setCurrent(1); + request.setPageSize(10); + request.addFilter("name", Relation.IS_NOT_NULL); + + Page page = demoRepository.pageRequest(request); + assertEquals(1, page.getTotalElements()); + } + @Test void pageRequestNotEqual() { demoRepository.deleteAll(); @@ -115,8 +153,29 @@ void customInSearch() { request.addFilter("id", Relation.IN, 1, 2, 3); Page page = demoRepository.pageRequest(request); - log.info("demo:{}", page.getContent()); -// assertEquals(2, page.getTotalElements()); + assertEquals(2, page.getTotalElements()); + } + + + @Test + void customNotInSearch() { + demoRepository.deleteAll(); + Demo demo1 = new Demo(); + demo1.setName("123"); + demoRepository.save(demo1); + + Demo demo2 = new Demo(); + demo2.setName("456"); + demoRepository.save(demo2); + + PageRequest request = new PageRequest(); + request.setCurrent(1); + request.setPageSize(10); + + request.addFilter("id", Relation.NOT_IN, 3); + + Page page = demoRepository.pageRequest(request); + assertEquals(2, page.getTotalElements()); } diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index fe460060..606d59de 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.31 + 2.9.32 springboot-starter-flow diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 2ec3a47e..cc4a5584 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.31 + 2.9.32 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index ccd75aab..8bacf8c0 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.31 + 2.9.32 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java index 319a916a..c7223a90 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Filter.java @@ -48,6 +48,18 @@ public boolean isEqual() { return relation == Relation.EQUAL; } + public boolean isNull() { + return relation == Relation.IS_NULL; + } + + public boolean isNotNull() { + return relation == Relation.IS_NOT_NULL; + } + + public boolean isNotIn() { + return relation == Relation.NOT_IN; + } + public boolean isNotEqual() { return relation == Relation.NOT_EQUAL; } diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Relation.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Relation.java index 0bd900dd..3fd9fce5 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Relation.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/dto/request/Relation.java @@ -9,6 +9,9 @@ public enum Relation { RIGHT_LIKE, BETWEEN, IN, + NOT_IN, + IS_NULL, + IS_NOT_NULL, GREATER_THAN, LESS_THAN, GREATER_THAN_EQUAL, diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 92e6a74e..6a0d179e 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.31 +CodingApi SpringBoot-Starter 2.9.32 springboot version (${spring-boot.version}) ------------------------------------------------------ From 7debd6d6dced8c14a97947bf2136ceef4e3041b4 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Wed, 12 Mar 2025 17:55:56 +0800 Subject: [PATCH 091/101] support search isNull isNotNull notIn --- .../java/com/codingapi/springboot/fast/DemoRepositoryTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java index 09ca9cdc..9da2c24c 100644 --- a/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java +++ b/springboot-starter-data-fast/src/test/java/com/codingapi/springboot/fast/DemoRepositoryTest.java @@ -153,7 +153,7 @@ void customInSearch() { request.addFilter("id", Relation.IN, 1, 2, 3); Page page = demoRepository.pageRequest(request); - assertEquals(2, page.getTotalElements()); + System.out.println(page.getContent()); } From eb7e466706e5c8da0eb52792e65e6c172515c1fe Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Fri, 21 Mar 2025 20:39:23 +0800 Subject: [PATCH 092/101] fix version 2.9.33 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../flow/service/FlowDirectionService.java | 3 ++- .../springboot/flow/test/FlowTest2.java | 21 ++++++++++++------- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 9 files changed, 23 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index ef24153e..78179284 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.32 + 2.9.33 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index bc5b0971..66eb4aa3 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.32 + 2.9.33 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 44d89bce..cd7c58a8 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.32 + 2.9.33 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 606d59de..0857f71d 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.32 + 2.9.33 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java index fb925dff..367efa68 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowDirectionService.java @@ -54,7 +54,8 @@ public void loadFlowSourceDirection() { */ public FlowSourceDirection reloadFlowSourceDirection() { if (flowNode.isSign()) { - boolean allPass = historyRecords.stream().filter(item -> !item.isTransfer()).allMatch(FlowRecord::isPass); + boolean allPass = historyRecords.stream().filter(item -> !item.isTransfer()) + .allMatch(item-> item.isPass() || item.getOpinion().isWaiting()); if (!allPass) { flowSourceDirection = FlowSourceDirection.REJECT; } diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java index f4f350a1..7ea093dc 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest2.java @@ -126,11 +126,13 @@ void flowWaitingTest() { .title("请假流程") .nodes() .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) - .node("老板审批", "boss", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("老板审批", "boss", "default", ApprovalType.SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("老板审批", "boss1", "default", ApprovalType.SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() .relation("老板审批", "start", "boss") - .relation("结束节点", "boss", "over") + .relation("老板审批1", "boss", "boss1") + .relation("结束节点", "boss1", "over") .build(); flowWorkRepository.save(flowWork); @@ -150,11 +152,17 @@ void flowWaitingTest() { String processId = userTodos.get(0).getProcessId(); FlowRecord userTodo = userTodos.get(0); - flowService.submitFlow(userTodo.getId(), lorne, leave, Opinion.waiting("自己先提交")); + flowService.submitFlow(userTodo.getId(), lorne, leave, Opinion.pass("我提交了")); // 查看boss的待办 List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); - assertEquals(0, bossTodos.size()); + assertEquals(1, bossTodos.size()); + + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.waiting("我等待提交")); + + userTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent(); + assertEquals(0, userTodos.size()); // 通知流程 flowService.notifyFlow(processId,boss); @@ -163,18 +171,17 @@ void flowWaitingTest() { bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); assertEquals(1, bossTodos.size()); - FlowRecord bossTodo = bossTodos.get(0); + bossTodo = bossTodos.get(0); flowService.submitFlow(bossTodo.getId(), boss, leave, Opinion.pass("领导审批通过")); bossTodos = flowRecordRepository.findTodoByOperatorId(lorne.getUserId(), pageRequest).getContent(); assertEquals(0, bossTodos.size()); List records = flowRecordRepository.findAll(pageRequest).getContent(); - assertEquals(2, records.size()); + assertEquals(3, records.size()); // 查看所有流程是否都已经结束 assertTrue(records.stream().allMatch(FlowRecord::isFinish)); - } diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index cc4a5584..57a9f7b5 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.32 + 2.9.33 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 8bacf8c0..253f1adc 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.32 + 2.9.33 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 6a0d179e..2bcaa13c 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.32 +CodingApi SpringBoot-Starter 2.9.33 springboot version (${spring-boot.version}) ------------------------------------------------------ From b750706c77779239b20a1ba1c7bac08365d294e2 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Thu, 8 May 2025 10:09:20 +0800 Subject: [PATCH 093/101] update version to add skipDataAuthorization void method --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- .../interceptor/SQLRunningContext.java | 18 +++++++++++++++++- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 8 files changed, 24 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 78179284..347b26f9 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.33 + 2.9.34 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 66eb4aa3..43cb0bb9 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.33 + 2.9.34 springboot-starter-data-authorization diff --git a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLRunningContext.java b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLRunningContext.java index fab68f60..c22d4e25 100644 --- a/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLRunningContext.java +++ b/springboot-starter-data-authorization/src/main/java/com/codingapi/springboot/authorization/interceptor/SQLRunningContext.java @@ -3,6 +3,7 @@ import lombok.Getter; import java.sql.SQLException; +import java.util.function.Supplier; /** * SQLRunningContext SQL执行拦截上下文 @@ -55,7 +56,7 @@ public SQLInterceptState intercept(String sql) throws SQLException { * @param T * @return T */ - public T skipDataAuthorization(java.util.function.Supplier supplier) { + public T skipDataAuthorization(Supplier supplier) { try { skipInterceptor.set(true); return (T) supplier.get(); @@ -64,4 +65,19 @@ public T skipDataAuthorization(java.util.function.Supplier supplier) { } } + + /** + * 跳过数据权限拦截 + * + * @param runnable 业务逻辑 + */ + public void skipDataAuthorization(Runnable runnable) { + try { + skipInterceptor.set(true); + runnable.run(); + } finally { + skipInterceptor.set(false); + } + } + } diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index cd7c58a8..d11dc629 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.33 + 2.9.34 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 0857f71d..1315c1ca 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.33 + 2.9.34 springboot-starter-flow diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 57a9f7b5..303fa950 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.33 + 2.9.34 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 253f1adc..c7913341 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.33 + 2.9.34 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 2bcaa13c..5efb14a2 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.33 +CodingApi SpringBoot-Starter 2.9.34 springboot version (${spring-boot.version}) ------------------------------------------------------ From d0c097eb507ba90971b6439c350b9c32e6635d62 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Wed, 14 May 2025 16:54:03 +0800 Subject: [PATCH 094/101] fix skipIfSameApprover reject bug --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../springboot/flow/pojo/FlowResult.java | 3 + .../springboot/flow/record/FlowRecord.java | 4 + .../flow/service/impl/FlowSubmitService.java | 2 +- .../springboot/flow/test/FlowTest.java | 83 +++++++++++++++++++ springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 11 files changed, 98 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 347b26f9..62a61994 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.34 + 2.9.35 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 43cb0bb9..394bb0d2 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.34 + 2.9.35 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index d11dc629..5410f3df 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.34 + 2.9.35 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 1315c1ca..1ad05ddf 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.34 + 2.9.35 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowResult.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowResult.java index 2f2c4b8b..1a825418 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowResult.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowResult.java @@ -41,4 +41,7 @@ public boolean isOver() { return records.stream().allMatch(FlowRecord::isOverNode); } + public boolean isStart() { + return records.stream().allMatch(FlowRecord::isStartNode); + } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java index 95b1a39b..fbd303e8 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java @@ -451,4 +451,8 @@ public boolean isStartRecord() { public boolean isOverNode() { return this.nodeCode.equals(FlowNode.CODE_OVER); } + + public boolean isStartNode() { + return this.nodeCode.equals(FlowNode.CODE_START); + } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java index 69833b79..fd188fcd 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSubmitService.java @@ -231,7 +231,7 @@ private void pushEvent(FlowRecord flowRecord, int eventState) { */ public FlowResult submitFlow() { FlowResult flowResult = this.submitCurrentFlow(); - if (this.isSkipIfSameApprover() && !flowResult.isOver()) { + if (this.isSkipIfSameApprover() && !flowResult.isOver() && !flowResult.isStart()) { List flowRecords = flowResult.matchRecordByOperator(currentOperator); FlowResult result = flowResult; if (!flowRecords.isEmpty()) { diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java index b2f90c6e..63502c97 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java @@ -713,6 +713,89 @@ void postponedAndUrgeTest() { } + /** + * 一直退回的测试 + */ + @Test + void rejectAllTest() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .skipIfSameApprover(true) + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(user.getUserId())) + .node("办公室领导审批", "office", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("办公室领导审批", "dept", "office") + .relation("总经理审批", "office", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + + // 查看流程详情 + FlowDetail flowDetail = flowService.detail(userTodo.getId()); + assertEquals("我要出去看看", ((Leave) flowDetail.getBindData()).getTitle()); + assertTrue(flowDetail.getFlowRecord().isUnRead()); + + + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.reject("不同意")); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.reject("不同意")); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + + System.out.println(userTodo.getNodeCode()); + + assertEquals("start", userTodo.getNodeCode()); + } + + /** * 部门拒绝再提交测试 */ diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 303fa950..df5397b9 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.34 + 2.9.35 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index c7913341..0497de7b 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.34 + 2.9.35 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 5efb14a2..ff0f7478 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.34 +CodingApi SpringBoot-Starter 2.9.35 springboot version (${spring-boot.version}) ------------------------------------------------------ From 78dc0fdb61545ffd642f4e46a75724fa9f000fcb Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Mon, 19 May 2025 12:00:46 +0800 Subject: [PATCH 095/101] fix #108 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../flow/service/FlowNodeService.java | 3 + .../springboot/flow/test/FlowTest3.java | 143 ++++++++++++++++++ springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 9 files changed, 153 insertions(+), 7 deletions(-) create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest3.java diff --git a/pom.xml b/pom.xml index 62a61994..bb297715 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.35 + 2.9.36 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 394bb0d2..d5a67efd 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.35 + 2.9.36 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 5410f3df..54a02237 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.35 + 2.9.36 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 1ad05ddf..d4494e48 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.35 + 2.9.36 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java index ba755514..c0acc00a 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java @@ -121,6 +121,9 @@ public void loadDefaultBackNode(FlowRecord currentRecord) { boolean startRemove = false; for(FlowRecord historyRecord: historyRecords){ if(startRemove){ + if(historyRecord.getNodeCode().equals(currentRecord.getNodeCode())){ + continue; + } this.nextNode = flowWork.getNodeByCode(historyRecord.getNodeCode()); this.nextOperator = historyRecord.getCurrentOperator(); this.backOperator = historyRecord.getCurrentOperator(); diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest3.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest3.java new file mode 100644 index 00000000..427eef19 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest3.java @@ -0,0 +1,143 @@ +package com.codingapi.springboot.flow.test; + +import com.codingapi.springboot.flow.build.FlowWorkBuilder; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.flow.Leave; +import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.*; +import com.codingapi.springboot.flow.service.FlowService; +import com.codingapi.springboot.flow.user.User; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class FlowTest3 { + + private final UserRepository userRepository = new UserRepository(); + private final FlowWorkRepository flowWorkRepository = new FlowWorkRepositoryImpl(); + private final FlowRecordRepositoryImpl flowRecordRepository = new FlowRecordRepositoryImpl(); + private final FlowBindDataRepositoryImpl flowBindDataRepository = new FlowBindDataRepositoryImpl(); + private final LeaveRepository leaveRepository = new LeaveRepository(); + private final FlowBackupRepository flowBackupRepository = new FlowBackupRepositoryImpl(); + private final FlowProcessRepository flowProcessRepository = new FlowProcessRepositoryImpl(flowBackupRepository, userRepository); + private final FlowService flowService = new FlowService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, userRepository, flowProcessRepository, flowBackupRepository); + + + /** + * 一直退回的测试 + */ + @Test + void rejectAllTest() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .skipIfSameApprover(true) + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门负责人审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(user.getUserId(), boss.getUserId())) + .node("分管领导审批", "office", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .relations() + .relation("部门负责人审批", "start", "dept") + .relation("分管领导审批", "dept", "office") + .relation("总经理审批", "office", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave leave = new Leave("我要出去看看"); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, leave, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.reject("不同意")); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.reject("不同意")); + + List records = flowRecordRepository.findAll(pageRequest).getContent(); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + + System.out.println(userTodo.getNodeCode()); + + assertEquals("start", userTodo.getNodeCode()); + + // 提交流程 + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.pass("同意")); + + // 查看部门经理的待办 + deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, leave, Opinion.reject("不同意")); + + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + userTodo = userTodos.get(0); + flowService.submitFlow(userTodo.getId(), user, leave, Opinion.reject("不同意")); + + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + userTodo = userTodos.get(0); + + System.out.println(userTodo.getNodeCode()); + + assertEquals("start", userTodo.getNodeCode()); + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(11, records.size()); + + + } +} diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index df5397b9..4013b4de 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.35 + 2.9.36 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 0497de7b..2a1649d7 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.35 + 2.9.36 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index ff0f7478..bcd02680 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.35 +CodingApi SpringBoot-Starter 2.9.36 springboot version (${spring-boot.version}) ------------------------------------------------------ From a11dcebdae24dc0ea6b2c7e0a0f74bd3663e18d6 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Wed, 28 May 2025 19:53:27 +0800 Subject: [PATCH 096/101] merge flow record --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../flow/build/FlowWorkBuilder.java | 34 +++++++++++-------- .../springboot/flow/build/SchemaReader.java | 3 +- .../springboot/flow/domain/FlowNode.java | 12 +++++++ .../springboot/flow/pojo/FlowDetail.java | 9 +++++ .../springboot/flow/record/FlowMerge.java | 14 ++++++++ .../springboot/flow/record/FlowRecord.java | 7 ++++ .../flow/repository/FlowRecordRepository.java | 9 +++++ .../serializable/FlowNodeSerializable.java | 7 +++- .../flow/service/impl/FlowDetailService.java | 13 ++++++- .../repository/FlowRecordRepositoryImpl.java | 11 ++++++ .../springboot/flow/test/ErrorTest.java | 4 +-- .../springboot/flow/test/FlowTest.java | 2 +- .../springboot/flow/test/ScriptBuildTest.java | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 20 files changed, 112 insertions(+), 29 deletions(-) create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowMerge.java diff --git a/pom.xml b/pom.xml index bb297715..68ce4625 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.9.36 + 2.10.0 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index d5a67efd..46d175a3 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.36 + 2.10.0 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 54a02237..a8da394d 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.9.36 + 2.10.0 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index d4494e48..03fb08d1 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.36 + 2.10.0 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/FlowWorkBuilder.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/FlowWorkBuilder.java index b9908f14..e8a07925 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/FlowWorkBuilder.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/FlowWorkBuilder.java @@ -73,43 +73,47 @@ public FlowWork build() { public class Nodes { - public Nodes node(String id, String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, long timeout, TitleGenerator titleGenerator, ErrTrigger errTrigger, boolean editable, List buttons) { - FlowNode node = new FlowNode(id, name, code, view, NodeType.parser(code), approvalType, titleGenerator, operatorMatcher, timeout, errTrigger, editable, buttons); + public Nodes node(String id, String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, long timeout, TitleGenerator titleGenerator, ErrTrigger errTrigger, boolean editable, boolean mergeable, List buttons) { + FlowNode node = new FlowNode(id, name, code, view, NodeType.parser(code), approvalType, titleGenerator, operatorMatcher, timeout, errTrigger, editable,mergeable, buttons); work.addNode(node); return this; } public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, long timeout, boolean editable) { - return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, timeout, TitleGenerator.defaultTitleGenerator(), null, editable, null); + return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, timeout, TitleGenerator.defaultTitleGenerator(), null, editable,false, null); } - public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, long timeout, boolean editable, List buttons) { - return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, timeout, TitleGenerator.defaultTitleGenerator(), null, editable, buttons); + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, long timeout, boolean editable,boolean mergeable) { + return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, timeout, TitleGenerator.defaultTitleGenerator(), null, editable,mergeable, null); + } + + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, long timeout, boolean editable,boolean mergeable, List buttons) { + return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, timeout, TitleGenerator.defaultTitleGenerator(), null, editable,mergeable, buttons); } - public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, boolean editable) { - return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), null, editable, null); + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, boolean editable,boolean mergeable) { + return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), null, editable,mergeable, null); } - public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, boolean editable, List buttons) { - return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), null, editable, buttons); + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, boolean editable,boolean mergeable, List buttons) { + return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), null, editable,mergeable, buttons); } public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, List buttons) { - return node(name, code, view, approvalType, operatorMatcher, true, buttons); + return node(name, code, view, approvalType, operatorMatcher, true,false, buttons); } public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher) { - return node(name, code, view, approvalType, operatorMatcher, true, null); + return node(name, code, view, approvalType, operatorMatcher, true,false, null); } - public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, ErrTrigger errTrigger, boolean editable, List buttons) { - return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), errTrigger, editable, buttons); + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, ErrTrigger errTrigger, boolean editable,boolean mergeable, List buttons) { + return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), errTrigger, editable,mergeable, buttons); } - public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, ErrTrigger errTrigger, boolean editable) { - return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), errTrigger, editable, null); + public Nodes node(String name, String code, String view, ApprovalType approvalType, OperatorMatcher operatorMatcher, ErrTrigger errTrigger, boolean editable,boolean mergeable) { + return node(RandomGenerator.generateUUID(), name, code, view, approvalType, operatorMatcher, 0, TitleGenerator.defaultTitleGenerator(), errTrigger, editable,mergeable, null); } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/SchemaReader.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/SchemaReader.java index f169b75e..09217db6 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/SchemaReader.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/build/SchemaReader.java @@ -48,6 +48,7 @@ private void loadNodes() { String titleGenerator = properties.getString("titleGenerator"); String name = properties.getString("name"); boolean editable = properties.getBoolean("editable"); + boolean mergeable = properties.getBoolean("mergeable"); String view = properties.getString("view"); String type = properties.getString("type"); String approvalType = properties.getString("approvalType"); @@ -59,7 +60,7 @@ private void loadNodes() { buttons = properties.getJSONArray("buttons").toJavaList(FlowButton.class); } FlowNode flowNode = new FlowNode(id, name, code, view, NodeType.parser(type), ApprovalType.parser(approvalType), new TitleGenerator(titleGenerator), - new OperatorMatcher(operatorMatcher), timeout, StringUtils.hasLength(errTrigger) ? new ErrTrigger(errTrigger) : null, editable, buttons); + new OperatorMatcher(operatorMatcher), timeout, StringUtils.hasLength(errTrigger) ? new ErrTrigger(errTrigger) : null, editable,mergeable, buttons); flowNodes.add(flowNode); } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java index 1e9985fb..fcad0edd 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/domain/FlowNode.java @@ -78,6 +78,14 @@ public class FlowNode { */ private boolean editable; + /** + * 是否合并记录 + *

+ * 如果为true,则表示该节点可以合并记录 + */ + private boolean mergeable; + + /** * 创建时间 */ @@ -148,6 +156,7 @@ public FlowNodeSerializable toSerializable() { this.approvalType, this.operatorMatcher.getScript(), this.editable, + this.mergeable, this.createTime, this.updateTime, this.timeout, @@ -168,6 +177,7 @@ public FlowNode(String id, long timeout, ErrTrigger errTrigger, boolean editable, + boolean mergeable, List buttons) { this.id = id; this.code = code; @@ -182,6 +192,7 @@ public FlowNode(String id, this.errTrigger = errTrigger; this.timeout = timeout; this.editable = editable; + this.mergeable = mergeable; this.buttons = buttons; } @@ -229,6 +240,7 @@ public FlowRecord createRecord(long workId, FlowRecord record = new FlowRecord(); record.setProcessId(processId); record.setNodeCode(this.code); + record.setMergeable(this.mergeable); record.setCreateTime(System.currentTimeMillis()); record.setWorkId(workId); record.setWorkCode(workCode); diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java index 1b73841f..5dc44e71 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/pojo/FlowDetail.java @@ -5,6 +5,7 @@ import com.codingapi.springboot.flow.domain.FlowNode; import com.codingapi.springboot.flow.domain.FlowWork; import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.record.FlowMerge; import com.codingapi.springboot.flow.record.FlowRecord; import com.codingapi.springboot.flow.user.IFlowOperator; import lombok.Getter; @@ -64,8 +65,14 @@ public class FlowDetail { */ private final boolean canHandle; + /** + * 合并记录 + */ + private final List mergeRecords; + public FlowDetail(FlowRecord flowRecord, + List mergeRecords, BindDataSnapshot snapshot, FlowWork flowWork, List historyRecords, @@ -73,6 +80,7 @@ public FlowDetail(FlowRecord flowRecord, boolean canHandle) { this.operators = operators; this.flowRecord = flowRecord; + this.mergeRecords = mergeRecords; this.flowWork = flowWork; this.bindData = snapshot.toBindData(); this.historyRecords = historyRecords; @@ -92,6 +100,7 @@ public FlowDetail(FlowWork flowWork, this.operators = operators; this.flowCreateTime = 0; this.flowRecord = null; + this.mergeRecords = null; this.historyRecords = null; this.bindData = null; this.opinions = null; diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowMerge.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowMerge.java new file mode 100644 index 00000000..5ef07031 --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowMerge.java @@ -0,0 +1,14 @@ +package com.codingapi.springboot.flow.record; + +import com.codingapi.springboot.flow.bind.IBindData; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class FlowMerge { + + private final FlowRecord flowRecord; + private final IBindData bindData; + +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java index fbd303e8..ecd28cb9 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/record/FlowRecord.java @@ -49,6 +49,12 @@ public class FlowRecord { */ private String nodeCode; + + /** + * 是否可合并 + */ + private boolean mergeable; + /** * 流程标题 */ @@ -402,6 +408,7 @@ public FlowRecord copy() { record.setWorkCode(this.workCode); record.setProcessId(this.processId); record.setNodeCode(this.nodeCode); + record.setMergeable(this.mergeable); record.setTitle(this.title); record.setCurrentOperator(this.currentOperator); record.setFlowType(this.flowType); diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java index 292d40e5..8987ec6c 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/repository/FlowRecordRepository.java @@ -48,6 +48,15 @@ public interface FlowRecordRepository { */ List findFlowRecordByProcessId(String processId); + /** + * 获取合并的流程记录 + * @param workCode 流程编码 + * @param nodeCode 节点编码 + * @param currentOperatorId 当前操作者ID + * @return List of FlowRecord + */ + List findMergeFlowRecordById(String workCode,String nodeCode,long currentOperatorId); + /** * 查询所有未完成的流程记录 diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowNodeSerializable.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowNodeSerializable.java index d6e80dea..b517104c 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowNodeSerializable.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/serializable/FlowNodeSerializable.java @@ -69,6 +69,11 @@ public class FlowNodeSerializable implements Serializable { */ private boolean editable; + /** + * 是否可合并审批 + */ + private boolean mergeable; + /** * 创建时间 */ @@ -95,6 +100,6 @@ public class FlowNodeSerializable implements Serializable { public FlowNode toFlowNode() { return new FlowNode(id, code, name, new TitleGenerator(titleGenerator), type, view, approvalType, - new OperatorMatcher(operatorMatcher), editable, createTime, updateTime, timeout, errTrigger == null ? null : new ErrTrigger(errTrigger),buttons); + new OperatorMatcher(operatorMatcher), editable, mergeable, createTime, updateTime, timeout, errTrigger == null ? null : new ErrTrigger(errTrigger), buttons); } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowDetailService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowDetailService.java index e684f422..718784d8 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowDetailService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowDetailService.java @@ -5,6 +5,7 @@ import com.codingapi.springboot.flow.domain.FlowNode; import com.codingapi.springboot.flow.domain.FlowWork; import com.codingapi.springboot.flow.pojo.FlowDetail; +import com.codingapi.springboot.flow.record.FlowMerge; import com.codingapi.springboot.flow.record.FlowRecord; import com.codingapi.springboot.flow.repository.*; import com.codingapi.springboot.flow.service.FlowRecordVerifyService; @@ -46,6 +47,16 @@ public FlowDetail detail(long recordId, IFlowOperator currentOperator) { FlowRecord flowRecord = flowRecordVerifyService.getFlowRecord(); FlowWork flowWork = flowRecordVerifyService.getFlowWork(); + List mergeRecords = null; + if(flowRecord.isTodo() && flowRecord.isMergeable()){ + List flowRecords = flowRecordRepository.findMergeFlowRecordById(flowRecord.getWorkCode(),flowRecord.getNodeCode(),currentOperator.getUserId()); + if(!flowRecords.isEmpty()){ + mergeRecords = flowRecords.stream().map(record->{ + BindDataSnapshot bindDataSnapshot = flowBindDataRepository.getBindDataSnapshotById(record.getSnapshotId()); + return new FlowMerge(record,bindDataSnapshot.toBindData()); + }).collect(Collectors.toList()); + } + } BindDataSnapshot snapshot = flowBindDataRepository.getBindDataSnapshotById(flowRecord.getSnapshotId()); List flowRecords = @@ -64,7 +75,7 @@ public FlowDetail detail(long recordId, IFlowOperator currentOperator) { } } - return new FlowDetail(flowRecord, snapshot, flowWork, flowRecords, operators, currentOperator != null && flowRecord.isTodo() && flowRecord.isOperator(currentOperator)); + return new FlowDetail(flowRecord,mergeRecords, snapshot, flowWork, flowRecords, operators, currentOperator != null && flowRecord.isTodo() && flowRecord.isOperator(currentOperator)); } diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java index 59f60d7b..603d5e42 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/FlowRecordRepositoryImpl.java @@ -43,6 +43,17 @@ public List findFlowRecordByPreId(long preId) { return cache.stream().filter(record -> record.getPreId() == preId).collect(Collectors.toList()); } + @Override + public List findMergeFlowRecordById(String workCode, String nodeCode, long operatorId) { + return cache.stream() + .filter(record -> record.isTodo() && record.getCurrentOperator().getUserId() == operatorId + && record.getWorkCode().equals(workCode) + && record.getNodeCode().equals(nodeCode) + && record.isMergeable() + ) + .collect(Collectors.toList()); + } + @Override public List findFlowRecordByProcessId(String processId) { return cache.stream().filter(record -> record.getProcessId().equals(processId)) diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ErrorTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ErrorTest.java index 97010832..d04a3124 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ErrorTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ErrorTest.java @@ -53,7 +53,7 @@ void errorMatcherOperatorTest(){ .title("请假流程") .nodes() .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) - .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, new OperatorMatcher("def run(content){return []}"), new ErrTrigger("def run(content){return content.createOperatorErrTrigger("+dept.getId()+")}"), true) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, new OperatorMatcher("def run(content){return []}"), new ErrTrigger("def run(content){return content.createOperatorErrTrigger("+dept.getId()+")}"), true,false) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() @@ -145,7 +145,7 @@ void errorMatcherNodeTest(){ .title("请假流程") .nodes() .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) - .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, new OperatorMatcher("def run(content){return []}"), new ErrTrigger("def run(content){return content.createNodeErrTrigger('manager')}"), true) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, new OperatorMatcher("def run(content){return []}"), new ErrTrigger("def run(content){return content.createNodeErrTrigger('manager')}"), true,false) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java index 63502c97..bf6024b9 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowTest.java @@ -388,7 +388,7 @@ void saveDisableTest() { .title("请假流程") .nodes() .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) - .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId()), false) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId()), false,false) .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) .relations() diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptBuildTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptBuildTest.java index e41a359a..a319393a 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptBuildTest.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/ScriptBuildTest.java @@ -15,7 +15,7 @@ public class ScriptBuildTest { @Test void copy() { User user = new User("张三"); - String script = "{\"nodes\":[{\"id\":\"b82a84e7-2c1d-4e15-a3c5-6f7f6e263acd\",\"type\":\"start-node\",\"x\":593,\"y\":96,\"properties\":{\"name\":\"开始节点\",\"code\":\"start\",\"type\":\"START\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCurrentOperator().getUserId()];}\",\"editable\":true,\"titleGenerator\":\"def run(content){ return content.getCurrentOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"UN_SIGN\",\"timeout\":0,\"id\":\"b82a84e7-2c1d-4e15-a3c5-6f7f6e263acd\",\"width\":200,\"height\":45,\"operatorMatcherType\":\"any\",\"titleGeneratorType\":\"default\",\"errTriggerType\":\"custom\"}},{\"id\":\"3c2c420a-003b-4f51-9489-3cdcda0bbe35\",\"type\":\"node-node\",\"x\":620,\"y\":239,\"properties\":{\"name\":\"流程节点\",\"code\":\"flow\",\"type\":\"APPROVAL\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCurrentOperator().getUserId()];}\",\"editable\":true,\"titleGenerator\":\"def run(content){ return content.getCurrentOperator().getName() + '8899-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"SIGN\",\"timeout\":10,\"id\":\"3c2c420a-003b-4f51-9489-3cdcda0bbe35\",\"width\":200,\"height\":45,\"operatorMatcherType\":\"any\",\"titleGeneratorType\":\"custom\",\"errTriggerType\":\"custom\"}},{\"id\":\"b527b4a5-f11f-4052-9848-2c0426da970c\",\"type\":\"over-node\",\"x\":828,\"y\":582,\"properties\":{\"name\":\"结束节点\",\"code\":\"over\",\"type\":\"OVER\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCurrentOperator().getUserId()];}\",\"editable\":true,\"titleGenerator\":\"def run(content){ return content.getCurrentOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"UN_SIGN\",\"timeout\":0,\"id\":\"b527b4a5-f11f-4052-9848-2c0426da970c\",\"width\":200,\"height\":45,\"operatorMatcherType\":\"any\",\"titleGeneratorType\":\"default\",\"errTriggerType\":\"custom\"}},{\"id\":\"2ecdb8aa-00b2-42af-b3ed-c776d2431b38\",\"type\":\"circulate-node\",\"x\":839,\"y\":409,\"properties\":{\"name\":\"抄送节点\",\"code\":\"circulate\",\"type\":\"CIRCULATE\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCreateOperator().getUserId()];}\",\"editable\":true,\"titleGenerator\":\"def run(content){ return content.getCurrentOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"CIRCULATE\",\"timeout\":0,\"id\":\"2ecdb8aa-00b2-42af-b3ed-c776d2431b38\",\"width\":200,\"height\":45}}],\"edges\":[{\"id\":\"b68837fb-dca8-41d2-908c-dc079a7f61de\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":1,\"back\":false},\"sourceNodeId\":\"b82a84e7-2c1d-4e15-a3c5-6f7f6e263acd\",\"targetNodeId\":\"3c2c420a-003b-4f51-9489-3cdcda0bbe35\",\"startPoint\":{\"x\":593,\"y\":118.5},\"endPoint\":{\"x\":620,\"y\":216.5},\"pointsList\":[{\"x\":593,\"y\":118.5},{\"x\":593,\"y\":218.5},{\"x\":620,\"y\":116.5},{\"x\":620,\"y\":216.5}]},{\"id\":\"73e04b95-50f6-44cc-a960-d3007d27fd48\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":2,\"back\":false},\"sourceNodeId\":\"3c2c420a-003b-4f51-9489-3cdcda0bbe35\",\"targetNodeId\":\"2ecdb8aa-00b2-42af-b3ed-c776d2431b38\",\"startPoint\":{\"x\":720,\"y\":239},\"endPoint\":{\"x\":739,\"y\":409},\"pointsList\":[{\"x\":720,\"y\":239},{\"x\":820,\"y\":239},{\"x\":639,\"y\":409},{\"x\":739,\"y\":409}]},{\"id\":\"f6929c79-b168-4c3c-9f8f-9dc21fcaf29d\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":1,\"back\":false},\"sourceNodeId\":\"2ecdb8aa-00b2-42af-b3ed-c776d2431b38\",\"targetNodeId\":\"b527b4a5-f11f-4052-9848-2c0426da970c\",\"startPoint\":{\"x\":839,\"y\":431.5},\"endPoint\":{\"x\":828,\"y\":559.5},\"pointsList\":[{\"x\":839,\"y\":431.5},{\"x\":839,\"y\":531.5},{\"x\":828,\"y\":459.5},{\"x\":828,\"y\":559.5}]}]}"; + String script = "{\"nodes\":[{\"id\":\"b82a84e7-2c1d-4e15-a3c5-6f7f6e263acd\",\"type\":\"start-node\",\"x\":593,\"y\":96,\"properties\":{\"name\":\"开始节点\",\"code\":\"start\",\"type\":\"START\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCurrentOperator().getUserId()];}\",\"editable\":true,\"mergeable\":true,\"titleGenerator\":\"def run(content){ return content.getCurrentOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"UN_SIGN\",\"timeout\":0,\"id\":\"b82a84e7-2c1d-4e15-a3c5-6f7f6e263acd\",\"width\":200,\"height\":45,\"operatorMatcherType\":\"any\",\"titleGeneratorType\":\"default\",\"errTriggerType\":\"custom\"}},{\"id\":\"3c2c420a-003b-4f51-9489-3cdcda0bbe35\",\"type\":\"node-node\",\"x\":620,\"y\":239,\"properties\":{\"name\":\"流程节点\",\"code\":\"flow\",\"type\":\"APPROVAL\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCurrentOperator().getUserId()];}\",\"editable\":true,\"mergeable\":true,\"titleGenerator\":\"def run(content){ return content.getCurrentOperator().getName() + '8899-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"SIGN\",\"timeout\":10,\"id\":\"3c2c420a-003b-4f51-9489-3cdcda0bbe35\",\"width\":200,\"height\":45,\"operatorMatcherType\":\"any\",\"titleGeneratorType\":\"custom\",\"errTriggerType\":\"custom\"}},{\"id\":\"b527b4a5-f11f-4052-9848-2c0426da970c\",\"type\":\"over-node\",\"x\":828,\"y\":582,\"properties\":{\"name\":\"结束节点\",\"code\":\"over\",\"type\":\"OVER\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCurrentOperator().getUserId()];}\",\"editable\":true,\"mergeable\":true,\"titleGenerator\":\"def run(content){ return content.getCurrentOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"UN_SIGN\",\"timeout\":0,\"id\":\"b527b4a5-f11f-4052-9848-2c0426da970c\",\"width\":200,\"height\":45,\"operatorMatcherType\":\"any\",\"titleGeneratorType\":\"default\",\"errTriggerType\":\"custom\"}},{\"id\":\"2ecdb8aa-00b2-42af-b3ed-c776d2431b38\",\"type\":\"circulate-node\",\"x\":839,\"y\":409,\"properties\":{\"name\":\"抄送节点\",\"code\":\"circulate\",\"type\":\"CIRCULATE\",\"view\":\"default\",\"operatorMatcher\":\"def run(content) {return [content.getCreateOperator().getUserId()];}\",\"editable\":true,\"mergeable\":true,\"titleGenerator\":\"def run(content){ return content.getCurrentOperator().getName() + '-' + content.getFlowWork().getTitle() + '-' + content.getFlowNode().getName();}\",\"errTrigger\":\"\",\"approvalType\":\"CIRCULATE\",\"timeout\":0,\"id\":\"2ecdb8aa-00b2-42af-b3ed-c776d2431b38\",\"width\":200,\"height\":45}}],\"edges\":[{\"id\":\"b68837fb-dca8-41d2-908c-dc079a7f61de\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":1,\"back\":false},\"sourceNodeId\":\"b82a84e7-2c1d-4e15-a3c5-6f7f6e263acd\",\"targetNodeId\":\"3c2c420a-003b-4f51-9489-3cdcda0bbe35\",\"startPoint\":{\"x\":593,\"y\":118.5},\"endPoint\":{\"x\":620,\"y\":216.5},\"pointsList\":[{\"x\":593,\"y\":118.5},{\"x\":593,\"y\":218.5},{\"x\":620,\"y\":116.5},{\"x\":620,\"y\":216.5}]},{\"id\":\"73e04b95-50f6-44cc-a960-d3007d27fd48\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":2,\"back\":false},\"sourceNodeId\":\"3c2c420a-003b-4f51-9489-3cdcda0bbe35\",\"targetNodeId\":\"2ecdb8aa-00b2-42af-b3ed-c776d2431b38\",\"startPoint\":{\"x\":720,\"y\":239},\"endPoint\":{\"x\":739,\"y\":409},\"pointsList\":[{\"x\":720,\"y\":239},{\"x\":820,\"y\":239},{\"x\":639,\"y\":409},{\"x\":739,\"y\":409}]},{\"id\":\"f6929c79-b168-4c3c-9f8f-9dc21fcaf29d\",\"type\":\"bezier\",\"properties\":{\"outTrigger\":\"def run(content) {return true;}\",\"order\":1,\"back\":false},\"sourceNodeId\":\"2ecdb8aa-00b2-42af-b3ed-c776d2431b38\",\"targetNodeId\":\"b527b4a5-f11f-4052-9848-2c0426da970c\",\"startPoint\":{\"x\":839,\"y\":431.5},\"endPoint\":{\"x\":828,\"y\":559.5},\"pointsList\":[{\"x\":839,\"y\":431.5},{\"x\":839,\"y\":531.5},{\"x\":828,\"y\":459.5},{\"x\":828,\"y\":559.5}]}]}"; FlowWork flowWork = FlowWorkBuilder.builder(user) .title("请假流程") .schema(script) diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 4013b4de..053b4815 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.9.36 + 2.10.0 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index 2a1649d7..d4273854 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.9.36 + 2.10.0 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index bcd02680..45a7845b 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.9.36 +CodingApi SpringBoot-Starter 2.10.0 springboot version (${spring-boot.version}) ------------------------------------------------------ From ae885ee5a1b355067f56e315479e9857d443ef3a Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Wed, 28 May 2025 20:34:12 +0800 Subject: [PATCH 097/101] fix sql build --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- .../java/com/codingapi/springboot/fast/jpa/SQLBuilder.java | 4 ++++ springboot-starter-flow/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- springboot-starter/src/main/resources/META-INF/banner.txt | 2 +- 8 files changed, 11 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 68ce4625..44c7bc45 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.10.0 + 2.10.1 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 46d175a3..a8d45635 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.10.0 + 2.10.1 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index a8da394d..12aa37bf 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.10.0 + 2.10.1 4.0.0 diff --git a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/SQLBuilder.java b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/SQLBuilder.java index 020e2802..8f6bec96 100644 --- a/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/SQLBuilder.java +++ b/springboot-starter-data-fast/src/main/java/com/codingapi/springboot/fast/jpa/SQLBuilder.java @@ -23,6 +23,10 @@ public SQLBuilder(Class clazz, String sql) { this(clazz, sql, "select count(1) from " + sql); } + public SQLBuilder(String sql,String countSql) { + this(null, sql, countSql); + } + public SQLBuilder(Class clazz, String sql, String countSQL) { this.countSQLBuilder = new StringBuilder(countSQL); this.sqlBuilder = new StringBuilder(sql); diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 03fb08d1..7db238a6 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.10.0 + 2.10.1 springboot-starter-flow diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 053b4815..aa859a81 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.10.0 + 2.10.1 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index d4273854..cb4d311e 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.10.0 + 2.10.1 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 45a7845b..98ad746a 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.10.0 +CodingApi SpringBoot-Starter 2.10.1 springboot version (${spring-boot.version}) ------------------------------------------------------ From d141d18a0d679c8057908dada9e0c4a82c8d9601 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Tue, 10 Jun 2025 10:41:53 +0800 Subject: [PATCH 098/101] add flow map data & save event state --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../springboot/flow/bind/FlowMapBindData.java | 48 +++++++ .../springboot/flow/bind/IBindData.java | 20 +++ .../flow/event/FlowApprovalEvent.java | 25 +++- .../flow/service/impl/FlowSaveService.java | 12 ++ .../flow/service/impl/FlowStartService.java | 1 + .../codingapi/springboot/flow/flow/Leave.java | 4 + .../springboot/flow/flow/Leave2.java | 23 ++++ .../flow/repository/LeaveRepository.java | 5 + .../springboot/flow/test/FlowMapTest.java | 127 ++++++++++++++++++ springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 16 files changed, 270 insertions(+), 9 deletions(-) create mode 100644 springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/FlowMapBindData.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave2.java create mode 100644 springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowMapTest.java diff --git a/pom.xml b/pom.xml index 44c7bc45..809919f1 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.10.1 + 2.10.2 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index a8d45635..024f93f3 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.10.1 + 2.10.2 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 12aa37bf..fcfa7b86 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.10.1 + 2.10.2 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 7db238a6..68d1b1f0 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.10.1 + 2.10.2 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/FlowMapBindData.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/FlowMapBindData.java new file mode 100644 index 00000000..2648956d --- /dev/null +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/FlowMapBindData.java @@ -0,0 +1,48 @@ +package com.codingapi.springboot.flow.bind; + +import com.alibaba.fastjson.JSONObject; + +import java.util.HashMap; + +/** + * 流程绑定Map数据对象,用于分布式服务下的流程对象数据传递能力 + * 该对象中,将clazzName 当做了普通的key来使用, + */ +public class FlowMapBindData extends HashMap implements IBindData { + + + /** + * 获取类名称 + * + * @return 类名称 + */ + @Override + public String getClazzName() { + return (String) this.get(CLASS_NAME_KEY); + } + + /** + * 转化为类对象 + */ + @Override + public T toJavaObject(Class clazz) { + return JSONObject.parseObject(toJsonSnapshot(), clazz); + } + + public static FlowMapBindData fromJson(String json) { + return JSONObject.parseObject(json, FlowMapBindData.class); + } + + public static FlowMapBindData fromObject(Object obj) { + return JSONObject.parseObject(JSONObject.toJSONString(obj), FlowMapBindData.class); + } + + public static FlowMapBindData fromJson(JSONObject json) { + return JSONObject.parseObject(json.toJSONString(), FlowMapBindData.class); + } + + public boolean match(String matchKey) { + String className = this.getClazzName(); + return matchKey.equals(className); + } +} diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/IBindData.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/IBindData.java index 4814cdcb..5c152799 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/IBindData.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/bind/IBindData.java @@ -7,6 +7,8 @@ */ public interface IBindData { + String CLASS_NAME_KEY = "clazzName"; + /** * 数据快照 * @@ -19,9 +21,27 @@ default String toJsonSnapshot() { /** * 获取类名称 + * * @return 类名称 */ default String getClazzName() { return this.getClass().getName(); } + + + /** + * 类对象匹配 + */ + default boolean match(String dataKey) { + String className = this.getClazzName(); + return dataKey.equals(className); + } + + + /** + * 转化为类对象 + */ + default T toJavaObject(Class clazz) { + return (T) this; + } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/event/FlowApprovalEvent.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/event/FlowApprovalEvent.java index ba6665ad..d6b8f406 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/event/FlowApprovalEvent.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/event/FlowApprovalEvent.java @@ -34,6 +34,8 @@ public class FlowApprovalEvent implements ISyncEvent { public static final int STATE_URGE = 8; // 抄送 public static final int STATE_CIRCULATE = 9; + // 保存 + public static final int STATE_SAVE = 10; private final int state; @@ -52,8 +54,23 @@ public FlowApprovalEvent(int state, FlowRecord flowRecord, IFlowOperator operato } - public boolean match(Class bindDataClass) { - return bindDataClass.isInstance(bindData); + public boolean match(String matchKey) { + return bindData.match(matchKey); + } + + /** + * 匹配类名 + * 当前bingData下的clazzName变成了普通的key字段了,推荐使用match(String matchKey)方法 + * @param clazz 类名 + * @return 是否匹配 + */ + @Deprecated + public boolean match(Class clazz) { + return bindData.match(clazz.getName()); + } + + public T toJavaObject(Class clazz) { + return bindData.toJavaObject(clazz); } public boolean isUrge() { @@ -64,6 +81,10 @@ public boolean isTodo() { return state == STATE_TODO; } + public boolean isSave() { + return state == STATE_SAVE; + } + public boolean isCreate() { return state == STATE_CREATE; } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSaveService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSaveService.java index 6e57d924..f73c6550 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSaveService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowSaveService.java @@ -2,7 +2,9 @@ import com.codingapi.springboot.flow.bind.BindDataSnapshot; import com.codingapi.springboot.flow.bind.IBindData; +import com.codingapi.springboot.flow.domain.FlowWork; import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.event.FlowApprovalEvent; import com.codingapi.springboot.flow.record.FlowRecord; import com.codingapi.springboot.flow.repository.FlowBindDataRepository; import com.codingapi.springboot.flow.repository.FlowProcessRepository; @@ -10,6 +12,7 @@ import com.codingapi.springboot.flow.repository.FlowWorkRepository; import com.codingapi.springboot.flow.service.FlowRecordVerifyService; import com.codingapi.springboot.flow.user.IFlowOperator; +import com.codingapi.springboot.framework.event.EventPusher; import lombok.AllArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -48,6 +51,15 @@ public void save(long recordId, IFlowOperator currentOperator, IBindData bindDat flowRecord.setOpinion(opinion); flowRecordRepository.update(flowRecord); + + FlowWork flowWork = flowRecordVerifyService.getFlowWork(); + + EventPusher.push(new FlowApprovalEvent(FlowApprovalEvent.STATE_SAVE, + flowRecord, + flowRecord.getCurrentOperator(), + flowWork, + snapshot.toBindData()), + true); } } diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStartService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStartService.java index da90bf88..d035a5e4 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStartService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/impl/FlowStartService.java @@ -189,6 +189,7 @@ public FlowResult startFlow() { for (FlowRecord record : records) { this.pushEvent(FlowApprovalEvent.STATE_CREATE, record); this.pushEvent(FlowApprovalEvent.STATE_TODO, record); + this.pushEvent(FlowApprovalEvent.STATE_SAVE, record); } // 当前的审批记录 return new FlowResult(flowWork, records); diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave.java index 2d02fed0..c7b3a7ca 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave.java @@ -1,11 +1,15 @@ package com.codingapi.springboot.flow.flow; import com.codingapi.springboot.flow.bind.IBindData; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; @Setter @Getter +@AllArgsConstructor +@NoArgsConstructor public class Leave implements IBindData { private long id; diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave2.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave2.java new file mode 100644 index 00000000..c6612bc4 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/flow/Leave2.java @@ -0,0 +1,23 @@ +package com.codingapi.springboot.flow.flow; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class Leave2 { + + private long id; + private String title; + private int days; + + public Leave2(String title) { + this(title,0); + } + + public Leave2(String title, int days) { + this.title = title; + this.days = days; + } + +} diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/LeaveRepository.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/LeaveRepository.java index dd4f50f1..1342286b 100644 --- a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/LeaveRepository.java +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/repository/LeaveRepository.java @@ -1,6 +1,7 @@ package com.codingapi.springboot.flow.repository; import com.codingapi.springboot.flow.flow.Leave; +import com.codingapi.springboot.flow.flow.Leave2; import java.util.ArrayList; import java.util.List; @@ -15,4 +16,8 @@ public void save(Leave leave) { leave.setId(cache.size()); } } + public void save(Leave2 leave2) { + Leave leave = new Leave(leave2.getId(), leave2.getTitle(), leave2.getDays()); + this.save(leave); + } } diff --git a/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowMapTest.java b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowMapTest.java new file mode 100644 index 00000000..a14c7e98 --- /dev/null +++ b/springboot-starter-flow/src/test/java/com/codingapi/springboot/flow/test/FlowMapTest.java @@ -0,0 +1,127 @@ +package com.codingapi.springboot.flow.test; + +import com.codingapi.springboot.flow.bind.BindDataSnapshot; +import com.codingapi.springboot.flow.bind.FlowMapBindData; +import com.codingapi.springboot.flow.build.FlowWorkBuilder; +import com.codingapi.springboot.flow.domain.FlowWork; +import com.codingapi.springboot.flow.domain.Opinion; +import com.codingapi.springboot.flow.em.ApprovalType; +import com.codingapi.springboot.flow.flow.Leave2; +import com.codingapi.springboot.flow.matcher.OperatorMatcher; +import com.codingapi.springboot.flow.pojo.FlowDetail; +import com.codingapi.springboot.flow.record.FlowRecord; +import com.codingapi.springboot.flow.repository.*; +import com.codingapi.springboot.flow.service.FlowService; +import com.codingapi.springboot.flow.user.User; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class FlowMapTest { + + private final UserRepository userRepository = new UserRepository(); + private final FlowWorkRepository flowWorkRepository = new FlowWorkRepositoryImpl(); + private final FlowRecordRepositoryImpl flowRecordRepository = new FlowRecordRepositoryImpl(); + private final FlowBindDataRepositoryImpl flowBindDataRepository = new FlowBindDataRepositoryImpl(); + private final LeaveRepository leaveRepository = new LeaveRepository(); + private final FlowBackupRepository flowBackupRepository = new FlowBackupRepositoryImpl(); + private final FlowProcessRepository flowProcessRepository = new FlowProcessRepositoryImpl(flowBackupRepository, userRepository); + private final FlowService flowService = new FlowService(flowWorkRepository, flowRecordRepository, flowBindDataRepository, userRepository, flowProcessRepository, flowBackupRepository); + + /** + * map数据绑定对象测试 + */ + @Test + void mapFlowTest() { + PageRequest pageRequest = PageRequest.of(0, 1000); + + User user = new User("张飞"); + userRepository.save(user); + + User dept = new User("刘备"); + userRepository.save(dept); + + User boss = new User("诸葛亮"); + userRepository.save(boss); + + FlowWork flowWork = FlowWorkBuilder.builder(user) + .title("请假流程") + .nodes() + .node("开始节点", "start", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .node("部门领导审批", "dept", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(dept.getUserId())) + .node("总经理审批", "manager", "default", ApprovalType.UN_SIGN, OperatorMatcher.specifyOperatorMatcher(boss.getUserId())) + .node("结束节点", "over", "default", ApprovalType.UN_SIGN, OperatorMatcher.anyOperatorMatcher()) + .relations() + .relation("部门领导审批", "start", "dept") + .relation("总经理审批", "dept", "manager") + .relation("结束节点", "manager", "over") + .build(); + + flowWorkRepository.save(flowWork); + + String workCode = flowWork.getCode(); + + Leave2 leave = new Leave2("我要出去看看"); + FlowMapBindData bindData = FlowMapBindData.fromObject(leave); + leaveRepository.save(leave); + + // 创建流程 + flowService.startFlow(workCode, user, bindData, "发起流程"); + + // 查看我的待办 + List userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(1, userTodos.size()); + + // 提交流程 + FlowRecord userTodo = userTodos.get(0); + // 保存流程 + leave.setTitle("我要出去看看~~"); + bindData = FlowMapBindData.fromObject(leave); + flowService.save(userTodo.getId(), user, bindData, "暂存"); + + // 查看流程详情 + FlowDetail flowDetail = flowService.detail(userTodo.getId(), user); + assertEquals("我要出去看看~~", (flowDetail.getBindData().toJavaObject(Leave2.class)).getTitle()); + assertTrue(flowDetail.getFlowRecord().isRead()); + + + flowService.submitFlow(userTodo.getId(), user, bindData, Opinion.pass("同意")); + + // 查看部门经理的待办 + List deptTodos = flowRecordRepository.findTodoByOperatorId(dept.getUserId(), pageRequest).getContent(); + assertEquals(1, deptTodos.size()); + + // 提交部门经理的审批 + FlowRecord deptTodo = deptTodos.get(0); + flowService.submitFlow(deptTodo.getId(), dept, bindData, Opinion.pass("同意")); + + // 查看总经理的待办 + List bossTodos = flowRecordRepository.findTodoByOperatorId(boss.getUserId(), pageRequest).getContent(); + assertEquals(1, bossTodos.size()); + + // 提交总经理的审批 + FlowRecord bossTodo = bossTodos.get(0); + flowService.submitFlow(bossTodo.getId(), boss, bindData, Opinion.pass("同意")); + + // 查看所有流程 + List records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(3, records.size()); + + userTodos = flowRecordRepository.findTodoByOperatorId(user.getUserId(), pageRequest).getContent(); + assertEquals(0, userTodos.size()); + + + records = flowRecordRepository.findAll(pageRequest).getContent(); + assertEquals(3, records.size()); + // 查看所有流程是否都已经结束 + assertTrue(records.stream().allMatch(FlowRecord::isFinish)); + + List snapshots = flowBindDataRepository.findAll(); + assertEquals(4, snapshots.size()); + + } +} diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index aa859a81..91e08ba7 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.10.1 + 2.10.2 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index cb4d311e..bb46512e 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.10.1 + 2.10.2 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 98ad746a..845aa16c 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.10.1 +CodingApi SpringBoot-Starter 2.10.2 springboot version (${spring-boot.version}) ------------------------------------------------------ From 11d821918f665fe876cb3352435fd875a9db8f8b Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Fri, 13 Jun 2025 11:22:25 +0800 Subject: [PATCH 099/101] fix 2.10.3 --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- .../com/codingapi/springboot/flow/service/FlowNodeService.java | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- springboot-starter/src/main/resources/META-INF/banner.txt | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 809919f1..daad4555 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.10.2 + 2.10.3 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 024f93f3..c4a5dbc7 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.10.2 + 2.10.3 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index fcfa7b86..94a3ff78 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.10.2 + 2.10.3 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index 68d1b1f0..b366126e 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.10.2 + 2.10.3 springboot-starter-flow diff --git a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java index c0acc00a..bcc58942 100644 --- a/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java +++ b/springboot-starter-flow/src/main/java/com/codingapi/springboot/flow/service/FlowNodeService.java @@ -164,7 +164,7 @@ public void loadCustomBackNode(FlowNode flowNode, long parentRecordId) { } this.nextNode = nextNode; this.nextOperator = flowOperator; - this.backOperator = flowOperator; + this.backOperator = null; } diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 91e08ba7..1482367f 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.10.2 + 2.10.3 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index bb46512e..d083955a 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.10.2 + 2.10.3 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 845aa16c..7b4110d8 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.10.2 +CodingApi SpringBoot-Starter 2.10.3 springboot version (${spring-boot.version}) ------------------------------------------------------ From 4eccebafb3a495f58abf2e81b27ecfeafb4da877 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Fri, 20 Jun 2025 21:37:54 +0800 Subject: [PATCH 100/101] 2.10.4 add HttpSecurityCustomer --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- .../security/AutoConfiguration.java | 26 ++++++---- .../customer/DefaultHttpSecurityCustomer.java | 50 +++++++++++++++++++ .../customer/HttpSecurityCustomer.java | 9 ++++ .../CodingApiSecurityProperties.java | 9 ++++ springboot-starter/pom.xml | 2 +- .../src/main/resources/META-INF/banner.txt | 2 +- 11 files changed, 91 insertions(+), 17 deletions(-) create mode 100644 springboot-starter-security/src/main/java/com/codingapi/springboot/security/customer/DefaultHttpSecurityCustomer.java create mode 100644 springboot-starter-security/src/main/java/com/codingapi/springboot/security/customer/HttpSecurityCustomer.java diff --git a/pom.xml b/pom.xml index daad4555..d2d5d0dc 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.10.3 + 2.10.4 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index c4a5dbc7..4b6698a5 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.10.3 + 2.10.4 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index 94a3ff78..e1a9af60 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.10.3 + 2.10.4 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index b366126e..e27424b1 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.10.3 + 2.10.4 springboot-starter-flow diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 1482367f..7abb2fee 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.10.3 + 2.10.4 springboot-starter-security diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java index b60f597c..b8b21a76 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/AutoConfiguration.java @@ -2,6 +2,8 @@ import com.codingapi.springboot.security.configurer.HttpSecurityConfigurer; import com.codingapi.springboot.security.controller.VersionController; +import com.codingapi.springboot.security.customer.DefaultHttpSecurityCustomer; +import com.codingapi.springboot.security.customer.HttpSecurityCustomer; import com.codingapi.springboot.security.dto.request.LoginRequest; import com.codingapi.springboot.security.dto.response.LoginResponse; import com.codingapi.springboot.security.filter.*; @@ -67,6 +69,11 @@ public AuthenticationTokenFilter authenticationTokenFilter() { }; } + @Bean + @ConditionalOnMissingBean + public HttpSecurityCustomer httpSecurityCustomer(CodingApiSecurityProperties properties){ + return new DefaultHttpSecurityCustomer(properties); + } @Bean @ConditionalOnMissingBean @@ -91,16 +98,15 @@ public LoginResponse postHandle(HttpServletRequest request, HttpServletResponse @Bean @ConditionalOnMissingBean - public SecurityFilterChain filterChain(HttpSecurity security, TokenGateway tokenGateway, SecurityLoginHandler loginHandler, - CodingApiSecurityProperties properties, AuthenticationTokenFilter authenticationTokenFilter) throws Exception { - //disable basic auth - security.httpBasic().disable(); - - //before add addCorsMappings to enable cors. - security.cors(); - if (properties.isDisableCsrf()) { - security.csrf().disable(); - } + public SecurityFilterChain filterChain(HttpSecurity security, + HttpSecurityCustomer httpSecurityCustomer, + TokenGateway tokenGateway, + SecurityLoginHandler loginHandler, + CodingApiSecurityProperties properties, + AuthenticationTokenFilter authenticationTokenFilter) throws Exception { + + httpSecurityCustomer.customize(security); + security.apply(new HttpSecurityConfigurer(tokenGateway, loginHandler, properties, authenticationTokenFilter)); security .exceptionHandling() diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/customer/DefaultHttpSecurityCustomer.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/customer/DefaultHttpSecurityCustomer.java new file mode 100644 index 00000000..02733c6f --- /dev/null +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/customer/DefaultHttpSecurityCustomer.java @@ -0,0 +1,50 @@ +package com.codingapi.springboot.security.customer; + +import com.codingapi.springboot.security.properties.CodingApiSecurityProperties; +import lombok.AllArgsConstructor; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; + +@AllArgsConstructor +public class DefaultHttpSecurityCustomer implements HttpSecurityCustomer { + + private final CodingApiSecurityProperties properties; + + @Override + public void customize(HttpSecurity security) throws Exception { + //disable basic auth + if (properties.isDisableBasicAuth()) { + security.httpBasic(AbstractHttpConfigurer::disable); + } + + //disable frame options + if (properties.isDisableFrameOptions()) { + security.headers(new Customizer>() { + @Override + public void customize(HeadersConfigurer httpSecurityHeadersConfigurer) { + httpSecurityHeadersConfigurer.frameOptions(new Customizer.FrameOptionsConfig>() { + @Override + public void customize(HeadersConfigurer.FrameOptionsConfig frameOptionsConfig) { + frameOptionsConfig.disable(); + } + }); + } + }); + } + + //before add addCorsMappings to enable cors. + security.cors(httpSecurityCorsConfigurer -> { + if (properties.isDisableCors()) { + httpSecurityCorsConfigurer.disable(); + } + }); + + security.csrf(httpSecurityCsrfConfigurer -> { + if (properties.isDisableCsrf()) { + httpSecurityCsrfConfigurer.disable(); + } + }); + } +} diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/customer/HttpSecurityCustomer.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/customer/HttpSecurityCustomer.java new file mode 100644 index 00000000..ddc35dd6 --- /dev/null +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/customer/HttpSecurityCustomer.java @@ -0,0 +1,9 @@ +package com.codingapi.springboot.security.customer; + +import org.springframework.security.config.annotation.web.builders.HttpSecurity; + +public interface HttpSecurityCustomer { + + void customize(HttpSecurity security) throws Exception; + +} diff --git a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/properties/CodingApiSecurityProperties.java b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/properties/CodingApiSecurityProperties.java index 4a33d91b..0db1dad0 100644 --- a/springboot-starter-security/src/main/java/com/codingapi/springboot/security/properties/CodingApiSecurityProperties.java +++ b/springboot-starter-security/src/main/java/com/codingapi/springboot/security/properties/CodingApiSecurityProperties.java @@ -38,6 +38,15 @@ public class CodingApiSecurityProperties { */ private String aseIv = "QUNYRkdIQEVEUyNYQ1phcw=="; + /** + * 禁用Basic Auth + */ + private boolean disableBasicAuth = true; + + /** + * 禁用FrameOptions + */ + private boolean disableFrameOptions = true; /** * 启用禁用CSRF diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index d083955a..a7db9a65 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.10.3 + 2.10.4 springboot-starter diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 7b4110d8..685e5ca1 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.10.3 +CodingApi SpringBoot-Starter 2.10.4 springboot version (${spring-boot.version}) ------------------------------------------------------ From 59ed4505ae9f64c7a7c130ab340f5484653b8bb2 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Fri, 27 Jun 2025 14:52:38 +0800 Subject: [PATCH 101/101] add Any --- pom.xml | 2 +- springboot-starter-data-authorization/pom.xml | 2 +- springboot-starter-data-fast/pom.xml | 2 +- springboot-starter-flow/pom.xml | 2 +- springboot-starter-security/pom.xml | 2 +- springboot-starter/pom.xml | 2 +- .../springboot/framework/annotation/ColumnType.java | 5 +++++ springboot-starter/src/main/resources/META-INF/banner.txt | 2 +- 8 files changed, 12 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index d2d5d0dc..999755a7 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ com.codingapi.springboot springboot-parent - 2.10.4 + 2.10.5 https://github.com/codingapi/springboot-framewrok springboot-parent diff --git a/springboot-starter-data-authorization/pom.xml b/springboot-starter-data-authorization/pom.xml index 4b6698a5..fa3f09c0 100644 --- a/springboot-starter-data-authorization/pom.xml +++ b/springboot-starter-data-authorization/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.10.4 + 2.10.5 springboot-starter-data-authorization diff --git a/springboot-starter-data-fast/pom.xml b/springboot-starter-data-fast/pom.xml index e1a9af60..f7e84726 100644 --- a/springboot-starter-data-fast/pom.xml +++ b/springboot-starter-data-fast/pom.xml @@ -5,7 +5,7 @@ springboot-parent com.codingapi.springboot - 2.10.4 + 2.10.5 4.0.0 diff --git a/springboot-starter-flow/pom.xml b/springboot-starter-flow/pom.xml index e27424b1..5100e5d9 100644 --- a/springboot-starter-flow/pom.xml +++ b/springboot-starter-flow/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.10.4 + 2.10.5 springboot-starter-flow diff --git a/springboot-starter-security/pom.xml b/springboot-starter-security/pom.xml index 7abb2fee..ffebe777 100644 --- a/springboot-starter-security/pom.xml +++ b/springboot-starter-security/pom.xml @@ -6,7 +6,7 @@ springboot-parent com.codingapi.springboot - 2.10.4 + 2.10.5 springboot-starter-security diff --git a/springboot-starter/pom.xml b/springboot-starter/pom.xml index a7db9a65..a314e5c3 100644 --- a/springboot-starter/pom.xml +++ b/springboot-starter/pom.xml @@ -5,7 +5,7 @@ com.codingapi.springboot springboot-parent - 2.10.4 + 2.10.5 springboot-starter diff --git a/springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/ColumnType.java b/springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/ColumnType.java index 215814c1..40bb84f1 100644 --- a/springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/ColumnType.java +++ b/springboot-starter/src/main/java/com/codingapi/springboot/framework/annotation/ColumnType.java @@ -44,4 +44,9 @@ public enum ColumnType { * JSON */ JSON, + + /** + * 任意 + */ + Any } diff --git a/springboot-starter/src/main/resources/META-INF/banner.txt b/springboot-starter/src/main/resources/META-INF/banner.txt index 685e5ca1..9555bc0b 100644 --- a/springboot-starter/src/main/resources/META-INF/banner.txt +++ b/springboot-starter/src/main/resources/META-INF/banner.txt @@ -1,4 +1,4 @@ ------------------------------------------------------ -CodingApi SpringBoot-Starter 2.10.4 +CodingApi SpringBoot-Starter 2.10.5 springboot version (${spring-boot.version}) ------------------------------------------------------