✨ flowable M1的适配版本
This commit is contained in:
1 parent
43da5a1dbf
commit
b69285c55b
256 files changed
+11295
-411
No files matched your search
+6
-1
@@ -48,6 +48,7 @@ import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -79,9 +80,13 @@ public class AuthorizationServerConfiguration {
|
||||
.authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint// 授权码端点个性化confirm页面
|
||||
.consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI)));
|
||||
|
||||
AntPathRequestMatcher[] requestMatchers = new AntPathRequestMatcher[] {
|
||||
AntPathRequestMatcher.antMatcher("/token/**"), AntPathRequestMatcher.antMatcher("/actuator/**"),
|
||||
AntPathRequestMatcher.antMatcher("/css/**"), AntPathRequestMatcher.antMatcher("/error") };
|
||||
|
||||
http.authorizeHttpRequests(authorizeRequests -> {
|
||||
// 自定义接口、端点暴露
|
||||
authorizeRequests.requestMatchers("/token/**", "/actuator/**", "/css/**", "/error").permitAll();
|
||||
authorizeRequests.requestMatchers(requestMatchers).permitAll();
|
||||
authorizeRequests.anyRequest().authenticated();
|
||||
})
|
||||
.apply(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现
|
||||
|
||||
@@ -38,6 +38,8 @@ import com.pig4cloud.pigx.common.security.annotation.Inner;
|
||||
import com.pig4cloud.pigx.common.security.service.PigxUser;
|
||||
import com.pig4cloud.pigx.common.security.util.OAuth2ErrorCodesExpand;
|
||||
import com.pig4cloud.pigx.common.security.util.OAuthClientException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -59,8 +61,6 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -114,8 +114,9 @@ public class PigxTokenEndpoint {
|
||||
@RequestParam(OAuth2ParameterNames.SCOPE) String scope,
|
||||
@RequestParam(OAuth2ParameterNames.STATE) String state) {
|
||||
SysOauthClientDetails clientDetails = RetOps
|
||||
.of(clientDetailsService.getClientDetailsById(clientId, SecurityConstants.FROM_IN)).getData()
|
||||
.orElseThrow(() -> new OAuthClientException("clientId 不合法"));
|
||||
.of(clientDetailsService.getClientDetailsById(clientId, SecurityConstants.FROM_IN))
|
||||
.getData()
|
||||
.orElseThrow(() -> new OAuthClientException("clientId 不合法"));
|
||||
|
||||
Set<String> authorizedScopes = StringUtils.commaDelimitedListToSet(clientDetails.getScope());
|
||||
modelAndView.addObject("clientId", clientId);
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ public class CustomeOAuth2AccessTokenGenerator implements OAuth2TokenGenerator<O
|
||||
@Override
|
||||
public OAuth2AccessToken generate(OAuth2TokenContext context) {
|
||||
if (!OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType()) || !OAuth2TokenFormat.REFERENCE
|
||||
.equals(context.getRegisteredClient().getTokenSettings().getAccessTokenFormat())) {
|
||||
.equals(context.getRegisteredClient().getTokenSettings().getAccessTokenFormat())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+6
-5
@@ -1,6 +1,7 @@
|
||||
package com.pig4cloud.pigx.auth.support.base;
|
||||
|
||||
import com.pig4cloud.pigx.common.security.util.OAuth2EndpointUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
@@ -9,7 +10,6 @@ import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@@ -83,10 +83,11 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationConverter<T extends O
|
||||
}
|
||||
|
||||
// 扩展信息
|
||||
Map<String, Object> additionalParameters = parameters.entrySet().stream()
|
||||
.filter(e -> !e.getKey().equals(OAuth2ParameterNames.GRANT_TYPE)
|
||||
&& !e.getKey().equals(OAuth2ParameterNames.SCOPE))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0)));
|
||||
Map<String, Object> additionalParameters = parameters.entrySet()
|
||||
.stream()
|
||||
.filter(e -> !e.getKey().equals(OAuth2ParameterNames.GRANT_TYPE)
|
||||
&& !e.getKey().equals(OAuth2ParameterNames.SCOPE))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0)));
|
||||
|
||||
// 创建token
|
||||
return buildToken(clientPrincipal, requestedScopes, additionalParameters);
|
||||
|
||||
+8
-3
@@ -23,9 +23,14 @@ public final class FormIdentityLoginConfigurer
|
||||
formLogin.failureHandler(new FormAuthenticationFailureHandler());
|
||||
formLogin.successHandler(new TenantSavedRequestAwareAuthenticationSuccessHandler());
|
||||
|
||||
}).logout() // SSO登出成功处理
|
||||
.logoutSuccessHandler(new SsoLogoutSuccessHandler()).deleteCookies("JSESSIONID")
|
||||
.invalidateHttpSession(true).and().csrf().disable();
|
||||
})
|
||||
.logout() // SSO登出成功处理
|
||||
.logoutSuccessHandler(new SsoLogoutSuccessHandler())
|
||||
.deleteCookies("JSESSIONID")
|
||||
.invalidateHttpSession(true)
|
||||
.and()
|
||||
.csrf()
|
||||
.disable();
|
||||
}
|
||||
|
||||
}
|
||||
+8
-7
@@ -74,13 +74,13 @@ public class PigxDaoAuthenticationProvider extends AbstractUserDetailsAuthentica
|
||||
if (authentication.getCredentials() == null) {
|
||||
this.logger.debug("Failed to authenticate since no credentials provided");
|
||||
throw new BadCredentialsException(this.messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
}
|
||||
String presentedPassword = authentication.getCredentials().toString();
|
||||
if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
|
||||
this.logger.debug("Failed to authenticate since password does not match stored value");
|
||||
throw new BadCredentialsException(this.messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,14 +96,15 @@ public class PigxDaoAuthenticationProvider extends AbstractUserDetailsAuthentica
|
||||
}
|
||||
|
||||
Map<String, PigxUserDetailsService> userDetailsServiceMap = SpringUtil
|
||||
.getBeansOfType(PigxUserDetailsService.class);
|
||||
.getBeansOfType(PigxUserDetailsService.class);
|
||||
|
||||
String finalClientId = clientId;
|
||||
Optional<PigxUserDetailsService> optional = userDetailsServiceMap.values().stream()
|
||||
.filter(service -> service.support(finalClientId, grantType))
|
||||
.max(Comparator.comparingInt(Ordered::getOrder));
|
||||
Optional<PigxUserDetailsService> optional = userDetailsServiceMap.values()
|
||||
.stream()
|
||||
.filter(service -> service.support(finalClientId, grantType))
|
||||
.max(Comparator.comparingInt(Ordered::getOrder));
|
||||
|
||||
if (!optional.isPresent()) {
|
||||
if (optional.isEmpty()) {
|
||||
throw new InternalAuthenticationServiceException("UserDetailsService error , not register");
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -26,6 +26,8 @@ import com.pig4cloud.pigx.common.core.util.KeyStrResolver;
|
||||
import com.pig4cloud.pigx.common.log.event.SysLogEvent;
|
||||
import com.pig4cloud.pigx.common.log.util.LogTypeEnum;
|
||||
import com.pig4cloud.pigx.common.log.util.SysLogUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -45,8 +47,6 @@ import org.springframework.security.web.authentication.AuthenticationSuccessHand
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Map;
|
||||
@@ -145,7 +145,8 @@ public class PigxAuthenticationSuccessEventHandler implements AuthenticationSucc
|
||||
Map<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters();
|
||||
|
||||
OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
|
||||
.tokenType(accessToken.getTokenType()).scopes(accessToken.getScopes());
|
||||
.tokenType(accessToken.getTokenType())
|
||||
.scopes(accessToken.getScopes());
|
||||
if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) {
|
||||
builder.expiresIn(ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()));
|
||||
}
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ public class OAuth2ResourceOwnerSmsAuthenticationProvider
|
||||
public void checkClient(RegisteredClient registeredClient) {
|
||||
assert registeredClient != null;
|
||||
if (!registeredClient.getAuthorizationGrantTypes()
|
||||
.contains(new AuthorizationGrantType(SecurityConstants.APP))) {
|
||||
.contains(new AuthorizationGrantType(SecurityConstants.APP))) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
<ijpay.version>2.9.6-17</ijpay.version>
|
||||
<groovy.version>3.0.3</groovy.version>
|
||||
<jsoup.version>1.13.1</jsoup.version>
|
||||
<aviator.version>5.3.3</aviator.version>
|
||||
<flowable.version>7.0.0.M1</flowable.version>
|
||||
<security.oauth.version>2.3.6.RELEASE</security.oauth.version>
|
||||
<fastjson.version>1.2.83</fastjson.version>
|
||||
@@ -166,7 +167,13 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-flowable-api</artifactId>
|
||||
<artifactId>pigx-flow-task-api</artifactId>
|
||||
<version>${pigx.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-flow-engine-api</artifactId>
|
||||
<version>${pigx.version}</version>
|
||||
</dependency>
|
||||
|
||||
@@ -256,6 +263,12 @@
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>${fastjson.version}</version>
|
||||
</dependency>
|
||||
<!--计算引擎-->
|
||||
<dependency>
|
||||
<groupId>com.googlecode.aviator</groupId>
|
||||
<artifactId>aviator</artifactId>
|
||||
<version>${aviator.version}</version>
|
||||
</dependency>
|
||||
<!-- 对象对比工具-->
|
||||
<dependency>
|
||||
<groupId>org.javers</groupId>
|
||||
|
||||
+10
-5
@@ -35,14 +35,19 @@ public interface ServiceNameConstants {
|
||||
*/
|
||||
String UPMS_SERVICE = "pigx-upms-biz";
|
||||
|
||||
/**
|
||||
* 分布式事务协调服务
|
||||
*/
|
||||
String TX_MANAGER = "pigx-tx-manager";
|
||||
|
||||
/**
|
||||
* app服务
|
||||
*/
|
||||
String APP_SERVER = "pigx-app-server-biz";
|
||||
|
||||
/**
|
||||
* 流程引擎
|
||||
*/
|
||||
String FLOW_ENGINE_SERVER = "pigx-flow-engine-biz";
|
||||
|
||||
/**
|
||||
* 流程工单
|
||||
*/
|
||||
String FLOW_TASK_SERVER = "pigx-flow-task-biz";
|
||||
|
||||
}
|
||||
+5
-2
@@ -50,8 +50,11 @@ public enum StyleTypeEnum {
|
||||
private String description;
|
||||
|
||||
public static String getDecs(String style) {
|
||||
return Arrays.stream(StyleTypeEnum.values()).filter(styleTypeEnum -> styleTypeEnum.getStyle().equals(style))
|
||||
.findFirst().get().getDescription();
|
||||
return Arrays.stream(StyleTypeEnum.values())
|
||||
.filter(styleTypeEnum -> styleTypeEnum.getStyle().equals(style))
|
||||
.findFirst()
|
||||
.get()
|
||||
.getDescription();
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -81,7 +81,7 @@ public class SensitiveSerialize extends JsonSerializer<String> implements Contex
|
||||
break;
|
||||
case CUSTOMER:
|
||||
jsonGenerator
|
||||
.writeString(DesensitizedUtils.desValue(origin, prefixNoMaskLen, suffixNoMaskLen, maskStr));
|
||||
.writeString(DesensitizedUtils.desValue(origin, prefixNoMaskLen, suffixNoMaskLen, maskStr));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknow sensitive type enum " + type);
|
||||
|
||||
@@ -93,4 +93,8 @@ public class R<T> implements Serializable {
|
||||
return apiResult;
|
||||
}
|
||||
|
||||
public boolean isOk() {
|
||||
return this.code == CommonConstants.SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
+3
-2
@@ -183,8 +183,9 @@ class DefaultRedisCacheWriter implements RedisCacheWriter {
|
||||
wasLocked = true;
|
||||
}
|
||||
|
||||
byte[][] keys = Optional.ofNullable(connection.keys(pattern)).orElse(Collections.emptySet())
|
||||
.toArray(new byte[0][]);
|
||||
byte[][] keys = Optional.ofNullable(connection.keys(pattern))
|
||||
.orElse(Collections.emptySet())
|
||||
.toArray(new byte[0][]);
|
||||
|
||||
if (keys.length > 0) {
|
||||
connection.del(keys);
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ public class RedisCacheAutoConfiguration {
|
||||
CacheProperties.Redis redisProperties = this.cacheProperties.getRedis();
|
||||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
|
||||
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair
|
||||
.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
|
||||
.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
|
||||
if (redisProperties.getTimeToLive() != null) {
|
||||
config = config.entryTtl(redisProperties.getTimeToLive());
|
||||
}
|
||||
|
||||
+8
-5
@@ -60,9 +60,12 @@ public class PigxDefaultDatascopeHandle implements DataScopeHandle {
|
||||
return true;
|
||||
}
|
||||
|
||||
List<String> roleIdList = user.getAuthorities().stream().map(GrantedAuthority::getAuthority)
|
||||
.filter(authority -> authority.startsWith(SecurityConstants.ROLE))
|
||||
.map(authority -> authority.split(StrUtil.UNDERLINE)[1]).collect(Collectors.toList());
|
||||
List<String> roleIdList = user.getAuthorities()
|
||||
.stream()
|
||||
.map(GrantedAuthority::getAuthority)
|
||||
.filter(authority -> authority.startsWith(SecurityConstants.ROLE))
|
||||
.map(authority -> authority.split(StrUtil.UNDERLINE)[1])
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Long> deptList = dataScope.getDeptList();
|
||||
|
||||
@@ -89,8 +92,8 @@ public class PigxDefaultDatascopeHandle implements DataScopeHandle {
|
||||
// 自定义
|
||||
if (DataScopeTypeEnum.CUSTOM.getType() == dsType && StrUtil.isNotBlank(role.getDsScope())) {
|
||||
String dsScope = role.getDsScope();
|
||||
deptList.addAll(
|
||||
Arrays.stream(dsScope.split(StrUtil.COMMA)).map(Long::parseLong).collect(Collectors.toList()));
|
||||
deptList
|
||||
.addAll(Arrays.stream(dsScope.split(StrUtil.COMMA)).map(Long::parseLong).collect(Collectors.toList()));
|
||||
}
|
||||
// 查询本级及其下级
|
||||
if (DataScopeTypeEnum.OWN_CHILD_LEVEL.getType() == dsType) {
|
||||
|
||||
+11
-5
@@ -22,6 +22,7 @@ import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.pig4cloud.pigx.common.core.exception.CheckedException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
@@ -29,7 +30,6 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -94,10 +94,16 @@ public class SqlFilterArgumentResolver implements HandlerMethodArgumentResolver
|
||||
}
|
||||
|
||||
List<OrderItem> orderItemList = new ArrayList<>();
|
||||
Optional.ofNullable(ascs).ifPresent(s -> orderItemList.addAll(Arrays.stream(s.split(StrUtil.COMMA))
|
||||
.filter(sqlInjectPredicate()).map(OrderItem::asc).collect(Collectors.toList())));
|
||||
Optional.ofNullable(descs).ifPresent(s -> orderItemList.addAll(Arrays.stream(s.split(StrUtil.COMMA))
|
||||
.filter(sqlInjectPredicate()).map(OrderItem::desc).collect(Collectors.toList())));
|
||||
Optional.ofNullable(ascs)
|
||||
.ifPresent(s -> orderItemList.addAll(Arrays.stream(s.split(StrUtil.COMMA))
|
||||
.filter(sqlInjectPredicate())
|
||||
.map(OrderItem::asc)
|
||||
.collect(Collectors.toList())));
|
||||
Optional.ofNullable(descs)
|
||||
.ifPresent(s -> orderItemList.addAll(Arrays.stream(s.split(StrUtil.COMMA))
|
||||
.filter(sqlInjectPredicate())
|
||||
.map(OrderItem::desc)
|
||||
.collect(Collectors.toList())));
|
||||
page.addOrder(orderItemList);
|
||||
|
||||
return page;
|
||||
|
||||
+4
-2
@@ -63,8 +63,10 @@ public enum DsJdbcUrlEnum {
|
||||
private final String description;
|
||||
|
||||
public static DsJdbcUrlEnum get(String dsType) {
|
||||
return Arrays.stream(DsJdbcUrlEnum.values()).filter(dsJdbcUrlEnum -> dsType.equals(dsJdbcUrlEnum.getDbName()))
|
||||
.findFirst().get();
|
||||
return Arrays.stream(DsJdbcUrlEnum.values())
|
||||
.filter(dsJdbcUrlEnum -> dsType.equals(dsJdbcUrlEnum.getDbName()))
|
||||
.findFirst()
|
||||
.get();
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -48,7 +48,7 @@ public class GrayRoundRobinLoadBalancer extends RoundRobinLoadBalancer {
|
||||
@Override
|
||||
public Mono<Response<ServiceInstance>> choose(Request request) {
|
||||
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
|
||||
.getIfAvailable(NoopServiceInstanceListSupplier::new);
|
||||
.getIfAvailable(NoopServiceInstanceListSupplier::new);
|
||||
return supplier.get(request).next().map(serviceInstances -> getInstanceResponse(serviceInstances, request));
|
||||
|
||||
}
|
||||
@@ -77,8 +77,8 @@ public class GrayRoundRobinLoadBalancer extends RoundRobinLoadBalancer {
|
||||
if (StrUtil.isBlank(reqVersion)) {
|
||||
// 过滤出不含VERSION实例
|
||||
List<ServiceInstance> versionInstanceList = instances.stream()
|
||||
.filter(instance -> !instance.getMetadata().containsKey(CommonConstants.VERSION))
|
||||
.collect(Collectors.toList());
|
||||
.filter(instance -> !instance.getMetadata().containsKey(CommonConstants.VERSION))
|
||||
.collect(Collectors.toList());
|
||||
if (CollUtil.isEmpty(versionInstanceList)) {
|
||||
// 根据权重获取实例
|
||||
return new DefaultResponse(randomByWeight(instances));
|
||||
|
||||
+2
-2
@@ -3,6 +3,7 @@ package com.pig4cloud.pigx.common.idempotent.aspect;
|
||||
import com.pig4cloud.pigx.common.idempotent.annotation.Idempotent;
|
||||
import com.pig4cloud.pigx.common.idempotent.exception.IdempotentException;
|
||||
import com.pig4cloud.pigx.common.idempotent.expression.KeyResolver;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.After;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
@@ -19,7 +20,6 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
@@ -58,7 +58,7 @@ public class IdempotentAspect {
|
||||
@Before("pointCut()")
|
||||
public void beforePointCut(JoinPoint joinPoint) {
|
||||
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
|
||||
.getRequestAttributes();
|
||||
.getRequestAttributes();
|
||||
HttpServletRequest request = requestAttributes.getRequest();
|
||||
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
|
||||
+3
-2
@@ -53,8 +53,9 @@ public class ExpressionResolver implements KeyResolver {
|
||||
Method method = signature.getMethod();
|
||||
if (method.getDeclaringClass().isInterface()) {
|
||||
try {
|
||||
method = joinPoint.getTarget().getClass().getDeclaredMethod(joinPoint.getSignature().getName(),
|
||||
method.getParameterTypes());
|
||||
method = joinPoint.getTarget()
|
||||
.getClass()
|
||||
.getDeclaredMethod(joinPoint.getSignature().getName(), method.getParameterTypes());
|
||||
}
|
||||
catch (SecurityException | NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
+7
-4
@@ -55,10 +55,13 @@ public class XxlJobAutoConfiguration {
|
||||
|
||||
// 如果配置为空则获取注册中心的服务列表 "http://pigx-xxl:9080/xxl-job-admin"
|
||||
if (!StringUtils.hasText(xxlJobProperties.getAdmin().getAddresses())) {
|
||||
String serverList = discoveryClient.getServices().stream().filter(s -> s.contains(XXL_JOB_ADMIN))
|
||||
.flatMap(s -> discoveryClient.getInstances(s).stream()).map(instance -> String
|
||||
.format("http://%s:%s/%s", instance.getHost(), instance.getPort(), XXL_JOB_ADMIN))
|
||||
.collect(Collectors.joining(","));
|
||||
String serverList = discoveryClient.getServices()
|
||||
.stream()
|
||||
.filter(s -> s.contains(XXL_JOB_ADMIN))
|
||||
.flatMap(s -> discoveryClient.getInstances(s).stream())
|
||||
.map(instance -> String.format("http://%s:%s/%s", instance.getHost(), instance.getPort(),
|
||||
XXL_JOB_ADMIN))
|
||||
.collect(Collectors.joining(","));
|
||||
xxlJobSpringExecutor.setAdminAddresses(serverList);
|
||||
}
|
||||
else {
|
||||
|
||||
+4
-2
@@ -43,8 +43,10 @@ public class LocalFileTemplate implements FileTemplate {
|
||||
*/
|
||||
@Override
|
||||
public List<Bucket> getAllBuckets() {
|
||||
return Arrays.stream(FileUtil.ls(properties.getLocal().getBasePath())).filter(FileUtil::isDirectory)
|
||||
.map(dir -> new Bucket(dir.getName())).collect(Collectors.toList());
|
||||
return Arrays.stream(FileUtil.ls(properties.getLocal().getBasePath()))
|
||||
.filter(FileUtil::isDirectory)
|
||||
.map(dir -> new Bucket(dir.getName()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+7
-3
@@ -228,9 +228,13 @@ public class OssTemplate implements InitializingBean, FileTemplate {
|
||||
AWSCredentials awsCredentials = new BasicAWSCredentials(properties.getOss().getAccessKey(),
|
||||
properties.getOss().getSecretKey());
|
||||
AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(awsCredentials);
|
||||
this.amazonS3 = AmazonS3Client.builder().withEndpointConfiguration(endpointConfiguration)
|
||||
.withClientConfiguration(clientConfiguration).withCredentials(awsCredentialsProvider)
|
||||
.disableChunkedEncoding().withPathStyleAccessEnabled(properties.getOss().getPathStyleAccess()).build();
|
||||
this.amazonS3 = AmazonS3Client.builder()
|
||||
.withEndpointConfiguration(endpointConfiguration)
|
||||
.withClientConfiguration(clientConfiguration)
|
||||
.withCredentials(awsCredentialsProvider)
|
||||
.disableChunkedEncoding()
|
||||
.withPathStyleAccessEnabled(properties.getOss().getPathStyleAccess())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
+4
-2
@@ -46,8 +46,10 @@ public class PermissionService {
|
||||
return false;
|
||||
}
|
||||
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
|
||||
return authorities.stream().map(GrantedAuthority::getAuthority).filter(StringUtils::hasText)
|
||||
.anyMatch(x -> PatternMatchUtils.simpleMatch(permissions, x));
|
||||
return authorities.stream()
|
||||
.map(GrantedAuthority::getAuthority)
|
||||
.filter(StringUtils::hasText)
|
||||
.anyMatch(x -> PatternMatchUtils.simpleMatch(permissions, x));
|
||||
}
|
||||
|
||||
}
|
||||
+8
-4
@@ -61,13 +61,17 @@ public class PermitAllUrlProperties implements InitializingBean {
|
||||
|
||||
// 获取方法上边的注解 替代path variable 为 *
|
||||
Inner method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Inner.class);
|
||||
Optional.ofNullable(method).ifPresent(inner -> Objects.requireNonNull(info.getPathPatternsCondition())
|
||||
.getPatternValues().forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, "*"))));
|
||||
Optional.ofNullable(method)
|
||||
.ifPresent(inner -> Objects.requireNonNull(info.getPathPatternsCondition())
|
||||
.getPatternValues()
|
||||
.forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, "*"))));
|
||||
|
||||
// 获取类上边的注解, 替代path variable 为 *
|
||||
Inner controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Inner.class);
|
||||
Optional.ofNullable(controller).ifPresent(inner -> Objects.requireNonNull(info.getPathPatternsCondition())
|
||||
.getPatternValues().forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, "*"))));
|
||||
Optional.ofNullable(controller)
|
||||
.ifPresent(inner -> Objects.requireNonNull(info.getPathPatternsCondition())
|
||||
.getPatternValues()
|
||||
.forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, "*"))));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.pig4cloud.pigx.common.security.component;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
@@ -26,7 +27,6 @@ import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -70,7 +70,7 @@ public class PigxBearerTokenExtractor implements BearerTokenResolver {
|
||||
if (authorizationHeaderToken != null) {
|
||||
if (parameterToken != null) {
|
||||
final BearerTokenError error = BearerTokenErrors
|
||||
.invalidRequest("Found multiple bearer tokens in the request");
|
||||
.invalidRequest("Found multiple bearer tokens in the request");
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
return authorizationHeaderToken;
|
||||
|
||||
+9
-7
@@ -49,12 +49,13 @@ public class PigxCustomOpaqueTokenIntrospector implements OpaqueTokenIntrospecto
|
||||
}
|
||||
|
||||
Map<String, PigxUserDetailsService> userDetailsServiceMap = SpringContextHolder
|
||||
.getBeansOfType(PigxUserDetailsService.class);
|
||||
.getBeansOfType(PigxUserDetailsService.class);
|
||||
|
||||
Optional<PigxUserDetailsService> optional = userDetailsServiceMap.values().stream()
|
||||
.filter(service -> service.support(Objects.requireNonNull(oldAuthorization).getRegisteredClientId(),
|
||||
oldAuthorization.getAuthorizationGrantType().getValue()))
|
||||
.max(Comparator.comparingInt(Ordered::getOrder));
|
||||
Optional<PigxUserDetailsService> optional = userDetailsServiceMap.values()
|
||||
.stream()
|
||||
.filter(service -> service.support(Objects.requireNonNull(oldAuthorization).getRegisteredClientId(),
|
||||
oldAuthorization.getAuthorizationGrantType().getValue()))
|
||||
.max(Comparator.comparingInt(Ordered::getOrder));
|
||||
|
||||
UserDetails userDetails = null;
|
||||
try {
|
||||
@@ -73,8 +74,9 @@ public class PigxCustomOpaqueTokenIntrospector implements OpaqueTokenIntrospecto
|
||||
|
||||
// 注入客户端信息,方便上下文中获取
|
||||
PigxUser pigxUser = (PigxUser) userDetails;
|
||||
Objects.requireNonNull(pigxUser).getAttributes().put(SecurityConstants.CLIENT_ID,
|
||||
oldAuthorization.getRegisteredClientId());
|
||||
Objects.requireNonNull(pigxUser)
|
||||
.getAttributes()
|
||||
.put(SecurityConstants.CLIENT_ID, oldAuthorization.getRegisteredClientId());
|
||||
return pigxUser;
|
||||
}
|
||||
|
||||
|
||||
+7
-3
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.pig4cloud.pigx.common.security.component;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -29,6 +28,7 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
@@ -53,9 +53,13 @@ public class PigxResourceServerConfiguration {
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
AntPathRequestMatcher[] requestMatchers = permitAllUrl.getIgnoreUrls()
|
||||
.stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.toList()
|
||||
.toArray(new AntPathRequestMatcher[] {});
|
||||
|
||||
http.authorizeHttpRequests(authorizeRequests -> authorizeRequests
|
||||
.requestMatchers(ArrayUtil.toArray(permitAllUrl.getIgnoreUrls(), String.class))
|
||||
http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(requestMatchers)
|
||||
.permitAll()
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
|
||||
+3
-3
@@ -19,8 +19,8 @@ public class PigxRedisOAuth2AuthorizationConsentService implements OAuth2Authori
|
||||
public void save(OAuth2AuthorizationConsent authorizationConsent) {
|
||||
Assert.notNull(authorizationConsent, "authorizationConsent cannot be null");
|
||||
|
||||
redisTemplate.opsForValue().set(buildKey(authorizationConsent), authorizationConsent, TIMEOUT,
|
||||
TimeUnit.MINUTES);
|
||||
redisTemplate.opsForValue()
|
||||
.set(buildKey(authorizationConsent), authorizationConsent, TIMEOUT, TimeUnit.MINUTES);
|
||||
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public class PigxRedisOAuth2AuthorizationConsentService implements OAuth2Authori
|
||||
Assert.hasText(registeredClientId, "registeredClientId cannot be empty");
|
||||
Assert.hasText(principalName, "principalName cannot be empty");
|
||||
return (OAuth2AuthorizationConsent) redisTemplate.opsForValue()
|
||||
.get(buildKey(registeredClientId, principalName));
|
||||
.get(buildKey(registeredClientId, principalName));
|
||||
}
|
||||
|
||||
private static String buildKey(String registeredClientId, String principalName) {
|
||||
|
||||
+14
-11
@@ -46,35 +46,38 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS
|
||||
if (isState(authorization)) {
|
||||
String token = authorization.getAttribute("state");
|
||||
redisTemplate.setValueSerializer(RedisSerializer.java());
|
||||
redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.STATE, token), authorization, TIMEOUT,
|
||||
TimeUnit.MINUTES);
|
||||
redisTemplate.opsForValue()
|
||||
.set(buildKey(OAuth2ParameterNames.STATE, token), authorization, TIMEOUT, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
if (isCode(authorization)) {
|
||||
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = authorization
|
||||
.getToken(OAuth2AuthorizationCode.class);
|
||||
.getToken(OAuth2AuthorizationCode.class);
|
||||
OAuth2AuthorizationCode authorizationCodeToken = authorizationCode.getToken();
|
||||
long between = ChronoUnit.MINUTES.between(authorizationCodeToken.getIssuedAt(),
|
||||
authorizationCodeToken.getExpiresAt());
|
||||
redisTemplate.setValueSerializer(RedisSerializer.java());
|
||||
redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.CODE, authorizationCodeToken.getTokenValue()),
|
||||
authorization, between, TimeUnit.MINUTES);
|
||||
redisTemplate.opsForValue()
|
||||
.set(buildKey(OAuth2ParameterNames.CODE, authorizationCodeToken.getTokenValue()), authorization,
|
||||
between, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
if (isRefreshToken(authorization)) {
|
||||
OAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken();
|
||||
long between = ChronoUnit.SECONDS.between(refreshToken.getIssuedAt(), refreshToken.getExpiresAt());
|
||||
redisTemplate.setValueSerializer(RedisSerializer.java());
|
||||
redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken.getTokenValue()),
|
||||
authorization, between, TimeUnit.SECONDS);
|
||||
redisTemplate.opsForValue()
|
||||
.set(buildKey(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken.getTokenValue()), authorization, between,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
if (isAccessToken(authorization)) {
|
||||
OAuth2AccessToken accessToken = authorization.getAccessToken().getToken();
|
||||
long between = ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt());
|
||||
redisTemplate.setValueSerializer(RedisSerializer.java());
|
||||
redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()),
|
||||
authorization, between, TimeUnit.SECONDS);
|
||||
redisTemplate.opsForValue()
|
||||
.set(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()), authorization, between,
|
||||
TimeUnit.SECONDS);
|
||||
|
||||
// 扩展记录 access-token 、username 的关系 1::token::username::admin::xxx
|
||||
String tokenUsername = String.format("%s::%s::%s::%s::%s", tenantKeyStrResolver.key(), AUTHORIZATION,
|
||||
@@ -95,7 +98,7 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS
|
||||
|
||||
if (isCode(authorization)) {
|
||||
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = authorization
|
||||
.getToken(OAuth2AuthorizationCode.class);
|
||||
.getToken(OAuth2AuthorizationCode.class);
|
||||
OAuth2AuthorizationCode authorizationCodeToken = authorizationCode.getToken();
|
||||
keys.add(buildKey(OAuth2ParameterNames.CODE, authorizationCodeToken.getTokenValue()));
|
||||
}
|
||||
@@ -143,7 +146,7 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS
|
||||
|
||||
private static boolean isCode(OAuth2Authorization authorization) {
|
||||
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = authorization
|
||||
.getToken(OAuth2AuthorizationCode.class);
|
||||
.getToken(OAuth2AuthorizationCode.class);
|
||||
return Objects.nonNull(authorizationCode);
|
||||
}
|
||||
|
||||
|
||||
+31
-24
@@ -88,46 +88,53 @@ public class PigxRemoteRegisteredClientRepository implements RegisteredClientRep
|
||||
public RegisteredClient findByClientId(String clientId) {
|
||||
|
||||
SysOauthClientDetails clientDetails = RetOps
|
||||
.of(clientDetailsService.getClientDetailsById(clientId, SecurityConstants.FROM_IN)).getData()
|
||||
.orElseThrow(() -> new OAuth2AuthorizationCodeRequestAuthenticationException(
|
||||
new OAuth2Error("客户端查询异常,请检查数据库链接"), null));
|
||||
.of(clientDetailsService.getClientDetailsById(clientId, SecurityConstants.FROM_IN))
|
||||
.getData()
|
||||
.orElseThrow(() -> new OAuth2AuthorizationCodeRequestAuthenticationException(
|
||||
new OAuth2Error("客户端查询异常,请检查数据库链接"), null));
|
||||
|
||||
RegisteredClient.Builder builder = RegisteredClient.withId(clientDetails.getClientId())
|
||||
.clientId(clientDetails.getClientId())
|
||||
.clientSecret(SecurityConstants.NOOP + clientDetails.getClientSecret())
|
||||
.clientAuthenticationMethods(clientAuthenticationMethods -> {
|
||||
clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
|
||||
clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_POST);
|
||||
});
|
||||
.clientId(clientDetails.getClientId())
|
||||
.clientSecret(SecurityConstants.NOOP + clientDetails.getClientSecret())
|
||||
.clientAuthenticationMethods(clientAuthenticationMethods -> {
|
||||
clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
|
||||
clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_POST);
|
||||
});
|
||||
// 授权模式
|
||||
Arrays.stream(clientDetails.getAuthorizedGrantTypes())
|
||||
.forEach(grant -> builder.authorizationGrantType(new AuthorizationGrantType(grant)));
|
||||
.forEach(grant -> builder.authorizationGrantType(new AuthorizationGrantType(grant)));
|
||||
|
||||
// 回调地址
|
||||
Optional.ofNullable(clientDetails.getWebServerRedirectUri()).ifPresent(redirectUri -> Arrays
|
||||
.stream(redirectUri.split(StrUtil.COMMA)).filter(StrUtil::isNotBlank).forEach(builder::redirectUri));
|
||||
Optional.ofNullable(clientDetails.getWebServerRedirectUri())
|
||||
.ifPresent(redirectUri -> Arrays.stream(redirectUri.split(StrUtil.COMMA))
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.forEach(builder::redirectUri));
|
||||
|
||||
// scope
|
||||
Optional.ofNullable(clientDetails.getScope()).ifPresent(
|
||||
scope -> Arrays.stream(scope.split(StrUtil.COMMA)).filter(StrUtil::isNotBlank).forEach(builder::scope));
|
||||
Optional.ofNullable(clientDetails.getScope())
|
||||
.ifPresent(scope -> Arrays.stream(scope.split(StrUtil.COMMA))
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.forEach(builder::scope));
|
||||
|
||||
// 注入扩展配置
|
||||
Optional.ofNullable(clientDetails.getAdditionalInformation()).ifPresent(ext -> {
|
||||
Map map = JSONUtil.parseObj(ext).toBean(Map.class);
|
||||
builder.clientSettings(ClientSettings.withSettings(map).requireProofKey(false)
|
||||
.requireAuthorizationConsent(!BooleanUtil.toBoolean(clientDetails.getAutoapprove())).build());
|
||||
builder.clientSettings(ClientSettings.withSettings(map)
|
||||
.requireProofKey(false)
|
||||
.requireAuthorizationConsent(!BooleanUtil.toBoolean(clientDetails.getAutoapprove()))
|
||||
.build());
|
||||
});
|
||||
|
||||
return builder
|
||||
.tokenSettings(TokenSettings.builder().accessTokenFormat(OAuth2TokenFormat.REFERENCE)
|
||||
.accessTokenTimeToLive(Duration.ofSeconds(Optional
|
||||
.ofNullable(clientDetails.getAccessTokenValidity()).orElse(accessTokenValiditySeconds)))
|
||||
.refreshTokenTimeToLive(
|
||||
Duration.ofSeconds(Optional.ofNullable(clientDetails.getRefreshTokenValidity())
|
||||
.orElse(refreshTokenValiditySeconds)))
|
||||
.build())
|
||||
.tokenSettings(TokenSettings.builder()
|
||||
.accessTokenFormat(OAuth2TokenFormat.REFERENCE)
|
||||
.accessTokenTimeToLive(Duration.ofSeconds(
|
||||
Optional.ofNullable(clientDetails.getAccessTokenValidity()).orElse(accessTokenValiditySeconds)))
|
||||
.refreshTokenTimeToLive(Duration.ofSeconds(Optional.ofNullable(clientDetails.getRefreshTokenValidity())
|
||||
.orElse(refreshTokenValiditySeconds)))
|
||||
.build())
|
||||
|
||||
.build();
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -104,7 +104,7 @@ public class PigxTocDefaultUserDetailsServiceImpl implements PigxUserDetailsServ
|
||||
|
||||
}
|
||||
Collection<? extends GrantedAuthority> authorities = AuthorityUtils
|
||||
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
|
||||
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
|
||||
AppUser user = info.getAppUser();
|
||||
// 构造security用户
|
||||
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ public class PigxTocMobileUserDetailsServiceImpl implements PigxUserDetailsServi
|
||||
|
||||
}
|
||||
Collection<? extends GrantedAuthority> authorities = AuthorityUtils
|
||||
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
|
||||
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
|
||||
AppUser user = info.getAppUser();
|
||||
// 构造security用户
|
||||
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ public interface PigxUserDetailsService extends UserDetailsService, Ordered {
|
||||
|
||||
}
|
||||
Collection<? extends GrantedAuthority> authorities = AuthorityUtils
|
||||
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
|
||||
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
|
||||
SysUser user = info.getSysUser();
|
||||
// 构造security用户
|
||||
|
||||
|
||||
+6
-5
@@ -1,6 +1,7 @@
|
||||
package com.pig4cloud.pigx.common.security.util;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.springframework.security.oauth2.core.*;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
||||
@@ -10,7 +11,6 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Map;
|
||||
@@ -29,15 +29,15 @@ public class OAuth2EndpointUtils {
|
||||
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(parameterMap.size());
|
||||
parameterMap.forEach((key, values) -> {
|
||||
for (String value : values) {
|
||||
parameters.add(key, value);
|
||||
}
|
||||
parameters.add(key, value);
|
||||
}
|
||||
});
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public boolean matchesPkceTokenRequest(HttpServletRequest request) {
|
||||
return AuthorizationGrantType.AUTHORIZATION_CODE.getValue()
|
||||
.equals(request.getParameter(OAuth2ParameterNames.GRANT_TYPE))
|
||||
.equals(request.getParameter(OAuth2ParameterNames.GRANT_TYPE))
|
||||
&& request.getParameter(OAuth2ParameterNames.CODE) != null
|
||||
&& request.getParameter(PkceParameterNames.CODE_VERIFIER) != null;
|
||||
}
|
||||
@@ -61,7 +61,8 @@ public class OAuth2EndpointUtils {
|
||||
OAuth2RefreshToken refreshToken = authentication.getRefreshToken().getToken();
|
||||
|
||||
OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
|
||||
.tokenType(accessToken.getTokenType()).scopes(accessToken.getScopes());
|
||||
.tokenType(accessToken.getTokenType())
|
||||
.scopes(accessToken.getScopes());
|
||||
if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) {
|
||||
builder.expiresIn(ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()));
|
||||
}
|
||||
|
||||
+6
-5
@@ -75,11 +75,12 @@ public class SecurityUtils {
|
||||
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
|
||||
|
||||
List<Long> roleIds = new ArrayList<>();
|
||||
authorities.stream().filter(granted -> StrUtil.startWith(granted.getAuthority(), SecurityConstants.ROLE))
|
||||
.forEach(granted -> {
|
||||
String id = StrUtil.removePrefix(granted.getAuthority(), SecurityConstants.ROLE);
|
||||
roleIds.add(Long.parseLong(id));
|
||||
});
|
||||
authorities.stream()
|
||||
.filter(granted -> StrUtil.startWith(granted.getAuthority(), SecurityConstants.ROLE))
|
||||
.forEach(granted -> {
|
||||
String id = StrUtil.removePrefix(granted.getAuthority(), SecurityConstants.ROLE);
|
||||
roleIds.add(Long.parseLong(id));
|
||||
});
|
||||
return roleIds;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -84,7 +84,7 @@ public class PigxSentinelInvocationHandler implements InvocationHandler {
|
||||
if (target instanceof Target.HardCodedTarget) {
|
||||
Target.HardCodedTarget hardCodedTarget = (Target.HardCodedTarget) target;
|
||||
MethodMetadata methodMetadata = SentinelContractHolder.METADATA_MAP
|
||||
.get(hardCodedTarget.type().getName() + Feign.configKey(hardCodedTarget.type(), method));
|
||||
.get(hardCodedTarget.type().getName() + Feign.configKey(hardCodedTarget.type(), method));
|
||||
// resource default is HttpMethod:protocol://url
|
||||
if (methodMetadata == null) {
|
||||
result = methodHandler.invoke(args);
|
||||
@@ -105,8 +105,8 @@ public class PigxSentinelInvocationHandler implements InvocationHandler {
|
||||
}
|
||||
if (fallbackFactory != null) {
|
||||
try {
|
||||
Object fallbackResult = fallbackMethodMap.get(method).invoke(fallbackFactory.create(ex),
|
||||
args);
|
||||
Object fallbackResult = fallbackMethodMap.get(method)
|
||||
.invoke(fallbackFactory.create(ex), args);
|
||||
return fallbackResult;
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
|
||||
+4
-2
@@ -43,8 +43,10 @@ public class SequenceAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnBean(SequenceSnowflakeProperties.class)
|
||||
public Sequence snowflakeSequence(SequenceSnowflakeProperties properties) {
|
||||
return SnowflakeSeqBuilder.create().datacenterId(properties.getDatacenterId())
|
||||
.workerId(properties.getWorkerId()).build();
|
||||
return SnowflakeSeqBuilder.create()
|
||||
.datacenterId(properties.getDatacenterId())
|
||||
.workerId(properties.getWorkerId())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -17,7 +17,7 @@ import java.sql.*;
|
||||
abstract class BaseDbHelper {
|
||||
|
||||
private static final SqlProviderFactory SQL_PROVIDER_FACTORY = SpringContextHolder
|
||||
.getBean(SqlProviderFactory.class);
|
||||
.getBean(SqlProviderFactory.class);
|
||||
|
||||
private static final long DELTA = 100000000L;
|
||||
|
||||
@@ -186,7 +186,7 @@ abstract class BaseDbHelper {
|
||||
conn = dataSource.getConnection();
|
||||
stmt = conn.createStatement();
|
||||
ResultSet resultSet = stmt
|
||||
.executeQuery(SQL_PROVIDER_FACTORY.getExistTableSql().replace("#tableName", tableName));
|
||||
.executeQuery(SQL_PROVIDER_FACTORY.getExistTableSql().replace("#tableName", tableName));
|
||||
|
||||
if (!resultSet.next()) {
|
||||
return false;
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ public class OpenAPIDefinitionImportSelector implements ImportBeanDefinitionRegi
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder openAPIMetadata = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(OpenAPIMetadataConfiguration.class);
|
||||
.genericBeanDefinition(OpenAPIMetadataConfiguration.class);
|
||||
openAPIMetadata.addPropertyValue("path", value);
|
||||
registry.registerBeanDefinition("openAPIMetadata", openAPIMetadata.getBeanDefinition());
|
||||
}
|
||||
|
||||
+3
-3
@@ -2,6 +2,7 @@ package com.pig4cloud.pigx.common.websocket.config;
|
||||
|
||||
import com.pig4cloud.pigx.common.websocket.handler.JsonMessageHandler;
|
||||
import com.pig4cloud.pigx.common.websocket.holder.JsonMessageHandlerHolder;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -11,7 +12,6 @@ import org.springframework.web.socket.config.annotation.EnableWebSocket;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
|
||||
import org.springframework.web.socket.server.HandshakeInterceptor;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -33,8 +33,8 @@ public class WebSocketAutoConfiguration {
|
||||
public WebSocketConfigurer webSocketConfigurer(List<HandshakeInterceptor> handshakeInterceptor,
|
||||
WebSocketHandler webSocketHandler) {
|
||||
return registry -> registry.addHandler(webSocketHandler, webSocketProperties.getPath())
|
||||
.setAllowedOrigins(webSocketProperties.getAllowOrigins())
|
||||
.addInterceptors(handshakeInterceptor.toArray(new HandshakeInterceptor[0]));
|
||||
.setAllowedOrigins(webSocketProperties.getAllowOrigins())
|
||||
.addInterceptors(handshakeInterceptor.toArray(new HandshakeInterceptor[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-2
@@ -70,8 +70,10 @@ public class PigxXssAutoConfiguration implements WebMvcConfigurer {
|
||||
patterns.add("/**");
|
||||
}
|
||||
XssCleanInterceptor interceptor = new XssCleanInterceptor(xssProperties);
|
||||
registry.addInterceptor(interceptor).addPathPatterns(patterns)
|
||||
.excludePathPatterns(xssProperties.getPathExcludePatterns()).order(Ordered.LOWEST_PRECEDENCE);
|
||||
registry.addInterceptor(interceptor)
|
||||
.addPathPatterns(patterns)
|
||||
.excludePathPatterns(xssProperties.getPathExcludePatterns())
|
||||
.order(Ordered.LOWEST_PRECEDENCE);
|
||||
}
|
||||
|
||||
}
|
||||
+4
-4
@@ -51,10 +51,10 @@ public class DefaultXssCleaner implements XssCleaner {
|
||||
else {
|
||||
// jsoup html 清理
|
||||
Document.OutputSettings outputSettings = new Document.OutputSettings()
|
||||
// 2. 转义,没找到关闭的方法,目前这个规则最少
|
||||
.escapeMode(Entities.EscapeMode.xhtml)
|
||||
// 3. 保留换行
|
||||
.prettyPrint(properties.isPrettyPrint());
|
||||
// 2. 转义,没找到关闭的方法,目前这个规则最少
|
||||
.escapeMode(Entities.EscapeMode.xhtml)
|
||||
// 3. 保留换行
|
||||
.prettyPrint(properties.isPrettyPrint());
|
||||
Document dirty = Jsoup.parseBodyFragment(bodyHtml, "");
|
||||
Cleaner cleaner = new Cleaner(XssUtil.WHITE_LIST);
|
||||
Document clean = cleaner.clean(dirty);
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-flow-engine</artifactId>
|
||||
<version>5.2.0-snapshot</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pigx-flow-engine-api</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<!--core 工具类-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>2.0.28</version>
|
||||
</dependency>
|
||||
<!--mybatis plus extension,包含了mybatis plus core-->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-extension</artifactId>
|
||||
</dependency>
|
||||
<!--feign 工具类-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-feign</artifactId>
|
||||
</dependency>
|
||||
<!-- excel 导入导出 -->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-excel</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
@author pigx archetype
|
||||
* <p>
|
||||
* feign client 存放目录,注意 @EnablePigxFeignClients 的扫描范围
|
||||
*/
|
||||
package com.pig4cloud.pigx.flow.engine.api.feign;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* @author pigx archetype
|
||||
* <p>
|
||||
* 常量和枚举定义
|
||||
*/
|
||||
package com.pig4cloud.pigx.flow.engine.constant;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* @author pigx archetype
|
||||
* <p>
|
||||
* DTO 存放目录
|
||||
*/
|
||||
package com.pig4cloud.pigx.flow.engine.dto;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* @author pigx archetype
|
||||
* <p>
|
||||
* 实体 存放目录
|
||||
*/
|
||||
package com.pig4cloud.pigx.flow.engine.entity;
|
||||
@@ -0,0 +1,134 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-flow-engine</artifactId>
|
||||
<version>5.2.0-snapshot</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pigx-flow-engine-biz</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<!-- flowable 核心依赖-->
|
||||
<dependency>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-bpmn-layout</artifactId>
|
||||
<version>${flowable.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-spring-boot-starter-process</artifactId>
|
||||
<version>${flowable.version}</version>
|
||||
</dependency>
|
||||
<!--task 模块接口-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-flow-task-api</artifactId>
|
||||
</dependency>
|
||||
<!--计算引擎-->
|
||||
<dependency>
|
||||
<groupId>com.googlecode.aviator</groupId>
|
||||
<artifactId>aviator</artifactId>
|
||||
</dependency>
|
||||
<!--必备: undertow容器-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-undertow</artifactId>
|
||||
</dependency>
|
||||
<!--必备: spring boot web-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<!--必备: 注册中心客户端-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
<!--必备: 配置中心客户端-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
<!--必备: 操作数据源相关-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-data</artifactId>
|
||||
</dependency>
|
||||
<!--必备:pigx安全模块-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-security</artifactId>
|
||||
</dependency>
|
||||
<!--必备:xss 过滤模块-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-xss</artifactId>
|
||||
</dependency>
|
||||
<!--必备: sentinel 依赖-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-sentinel</artifactId>
|
||||
</dependency>
|
||||
<!--必备: feign 依赖-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-feign</artifactId>
|
||||
</dependency>
|
||||
<!--必备: 依赖api模块-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-flow-engine-api</artifactId>
|
||||
</dependency>
|
||||
<!--必备: log 依赖-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-log</artifactId>
|
||||
</dependency>
|
||||
<!--选配: mybatis 依赖 -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<!--选配: druid 连接池 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-3-starter</artifactId>
|
||||
</dependency>
|
||||
<!--选配: mysql 数据库驱动 -->
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
<!--选配: swagger文档-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-swagger</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-cache</artifactId>
|
||||
</dependency>
|
||||
<!--测试: spring boot test-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.pig4cloud.pigx.flow.engine;
|
||||
|
||||
import com.pig4cloud.pigx.common.feign.annotation.EnablePigxFeignClients;
|
||||
import com.pig4cloud.pigx.common.security.annotation.EnablePigxResourceServer;
|
||||
import com.pig4cloud.pigx.common.swagger.annotation.EnableOpenApi;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
|
||||
/**
|
||||
* @author pigx archetype
|
||||
* <p>
|
||||
* 项目启动类
|
||||
*/
|
||||
@EnableOpenApi("engine")
|
||||
@EnablePigxFeignClients
|
||||
@EnableDiscoveryClient
|
||||
@EnablePigxResourceServer
|
||||
@SpringBootApplication
|
||||
public class PigxFlowEngineApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(PigxFlowEngineApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
+369
@@ -0,0 +1,369 @@
|
||||
package com.pig4cloud.pigx.flow.engine.controller;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.flow.engine.utils.ModelUtil;
|
||||
import com.pig4cloud.pigx.flow.task.dto.*;
|
||||
import com.pig4cloud.pigx.flow.task.entity.Process;
|
||||
import com.pig4cloud.pigx.flow.task.utils.NodeUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.BpmnAutoLayout;
|
||||
import org.flowable.bpmn.converter.BpmnXMLConverter;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.common.engine.impl.identity.Authentication;
|
||||
import org.flowable.engine.HistoryService;
|
||||
import org.flowable.engine.RepositoryService;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.flowable.engine.history.HistoricActivityInstance;
|
||||
import org.flowable.engine.history.HistoricActivityInstanceQuery;
|
||||
import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.flowable.engine.runtime.Execution;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
|
||||
import org.flowable.task.api.TaskQuery;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 工作流控制器 负责流程模型的创建、启动、审批等功能
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/flow")
|
||||
public class EngineFlowController {
|
||||
|
||||
private final TaskService taskService;
|
||||
|
||||
private final HistoryService historyService;
|
||||
|
||||
private final RepositoryService repositoryService;
|
||||
|
||||
private final RuntimeService runtimeService;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* 创建流程定义
|
||||
* @param map 创建参数
|
||||
* @return 流程定义ID
|
||||
*/
|
||||
@PostMapping("create")
|
||||
@SneakyThrows
|
||||
public R create(@RequestBody Map<String, Object> map) {
|
||||
|
||||
Long userId = MapUtil.getLong(map, "userId");
|
||||
|
||||
Process process = MapUtil.get(map, "process", Process.class);
|
||||
|
||||
String flowId = "P" + userId + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN)
|
||||
+ RandomUtil.randomString(5);
|
||||
log.info("flowId={}", flowId);
|
||||
BpmnModel bpmnModel = ModelUtil.buildBpmnModel(objectMapper.readValue(process.getProcess(), Node.class),
|
||||
process.getName(), flowId);
|
||||
{
|
||||
byte[] bpmnBytess = new BpmnXMLConverter().convertToXML(bpmnModel);
|
||||
String filename = "/tmp/flowable-deployment/" + flowId + ".bpmn20.xml";
|
||||
log.debug("部署时的模型文件:{}", filename);
|
||||
FileUtil.writeBytes(bpmnBytess, filename);
|
||||
}
|
||||
repositoryService.createDeployment().addBpmnModel(StrUtil.format("{}.bpmn20.xml", "test1"), bpmnModel).deploy();
|
||||
|
||||
return R.ok(flowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动流程实例
|
||||
* @param processInstanceParamDto 启动参数
|
||||
* @return 流程实例ID
|
||||
*/
|
||||
@PostMapping("/start")
|
||||
public R start(@RequestBody ProcessInstanceParamDto processInstanceParamDto) {
|
||||
Authentication.setAuthenticatedUserId(processInstanceParamDto.getStartUserId());
|
||||
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processInstanceParamDto.getFlowId(),
|
||||
processInstanceParamDto.getParamMap());
|
||||
|
||||
String processInstanceId = processInstance.getProcessInstanceId();
|
||||
return R.ok(processInstanceId);
|
||||
|
||||
}
|
||||
|
||||
@GetMapping("/showImg")
|
||||
public R showImg(String procInsId) {
|
||||
|
||||
String procDefId;
|
||||
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
|
||||
.processInstanceId(procInsId)
|
||||
.singleResult();
|
||||
if (processInstance == null) {
|
||||
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
|
||||
.processInstanceId(procInsId)
|
||||
.singleResult();
|
||||
procDefId = historicProcessInstance.getProcessDefinitionId();
|
||||
|
||||
}
|
||||
else {
|
||||
procDefId = processInstance.getProcessDefinitionId();
|
||||
}
|
||||
|
||||
// 使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
|
||||
BpmnModel bpmnModel = repositoryService.getBpmnModel(procDefId);
|
||||
|
||||
// 创建默认的流程图生成器
|
||||
DefaultProcessDiagramGenerator defaultProcessDiagramGenerator = new DefaultProcessDiagramGenerator();
|
||||
// 生成图片的类型
|
||||
String imageType = "png";
|
||||
// 高亮节点集合
|
||||
List<String> highLightedActivities = new ArrayList<>();
|
||||
// 高亮连线集合
|
||||
List<String> highLightedFlows = new ArrayList<>();
|
||||
// 查询所有历史节点信息
|
||||
List<HistoricActivityInstance> hisActInsList = historyService.createHistoricActivityInstanceQuery()
|
||||
.processInstanceId(procInsId)
|
||||
.list();
|
||||
|
||||
// 遍历
|
||||
hisActInsList.forEach(historicActivityInstance -> {
|
||||
if ("sequenceFlow".equals(historicActivityInstance.getActivityType())) {
|
||||
// 添加高亮连线
|
||||
highLightedFlows.add(historicActivityInstance.getActivityId());
|
||||
}
|
||||
else {
|
||||
// 添加高亮节点
|
||||
highLightedActivities.add(historicActivityInstance.getActivityId());
|
||||
}
|
||||
});
|
||||
// 节点字体
|
||||
String activityFontName = "宋体";
|
||||
// 连线标签字体
|
||||
String labelFontName = "微软雅黑";
|
||||
// 连线标签字体
|
||||
String annotationFontName = "宋体";
|
||||
// 类加载器
|
||||
ClassLoader customClassLoader = null;
|
||||
// 比例因子,默认即可
|
||||
double scaleFactor = 1.0d;
|
||||
// 不设置连线标签不会画
|
||||
boolean drawSequenceFlowNameWithNoLabelDI = true;
|
||||
|
||||
BpmnAutoLayout bpmnAutoLayout = new BpmnAutoLayout(bpmnModel);
|
||||
bpmnAutoLayout.setTaskHeight(120);
|
||||
bpmnAutoLayout.setTaskWidth(120);
|
||||
bpmnAutoLayout.execute();
|
||||
// 生成图片
|
||||
InputStream inputStream = defaultProcessDiagramGenerator.generateDiagram(bpmnModel, imageType,
|
||||
highLightedActivities, highLightedFlows, activityFontName, labelFontName, annotationFontName, null,
|
||||
scaleFactor, drawSequenceFlowNameWithNoLabelDI); // 获取输入流
|
||||
|
||||
String content = Base64.encode(inputStream);
|
||||
return R.ok(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批任务
|
||||
* @param taskId 任务ID
|
||||
* @param approved 是否通过
|
||||
*/
|
||||
@PostMapping("/approve")
|
||||
public void approve(String taskId, boolean approved) {
|
||||
Map<String, Object> variables = new HashMap<>();
|
||||
variables.put("approved", approved);
|
||||
variables.put("ko", 10);
|
||||
variables.put("assigneeListSub", CollUtil.newArrayList("aa", "bb"));
|
||||
taskService.complete(taskId, variables);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止流程实例
|
||||
* @param taskParamDto 参数
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PostMapping("stopProcessInstance")
|
||||
public R stopProcessInstance(@RequestBody TaskParamDto taskParamDto) {
|
||||
List<String> processInstanceIdList = taskParamDto.getProcessInstanceIdList();
|
||||
processInstanceIdList.forEach(processInstanceId -> {
|
||||
// 查询流程实例
|
||||
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
|
||||
.processInstanceId(processInstanceId)
|
||||
.singleResult();
|
||||
if (Optional.ofNullable(processInstance).isPresent()) {
|
||||
// 查询执行实例
|
||||
List<String> executionIds = runtimeService.createExecutionQuery()
|
||||
.parentId(processInstanceId)
|
||||
.list()
|
||||
.stream()
|
||||
.map(Execution::getId)
|
||||
.collect(Collectors.toList());
|
||||
// 更改活动状态为结束
|
||||
runtimeService.createChangeActivityStateBuilder()
|
||||
.moveExecutionsToSingleActivityId(executionIds, "end")
|
||||
.changeState();
|
||||
}
|
||||
});
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户已办任务
|
||||
* @param taskQueryParamDto 查询参数
|
||||
* @return 分页任务信息
|
||||
*/
|
||||
@PostMapping("/queryCompletedTask")
|
||||
public R queryCompletedTask(@RequestBody TaskQueryParamDto taskQueryParamDto) {
|
||||
HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService
|
||||
.createHistoricActivityInstanceQuery();
|
||||
HistoricActivityInstanceQuery activityInstanceQuery = historicActivityInstanceQuery
|
||||
.taskAssignee(taskQueryParamDto.getAssign())
|
||||
.finished()
|
||||
.orderByHistoricActivityInstanceEndTime()
|
||||
.desc();
|
||||
|
||||
if (ArrayUtil.isNotEmpty(taskQueryParamDto.getTaskTime())) {
|
||||
ZoneId zoneId = ZoneId.systemDefault();
|
||||
ZonedDateTime zonedBeforeDateTime = taskQueryParamDto.getTaskTime()[0].atZone(zoneId);
|
||||
ZonedDateTime zonedAfterDateTime = taskQueryParamDto.getTaskTime()[1].atZone(zoneId);
|
||||
Date beforeDate = Date.from(zonedBeforeDateTime.toInstant());
|
||||
Date afterDate = Date.from(zonedAfterDateTime.toInstant());
|
||||
activityInstanceQuery.finishedBefore(afterDate).finishedAfter(beforeDate);
|
||||
}
|
||||
|
||||
List<HistoricActivityInstance> list = activityInstanceQuery.listPage(
|
||||
(taskQueryParamDto.getPageNum() - 1) * taskQueryParamDto.getPageSize(),
|
||||
taskQueryParamDto.getPageSize());
|
||||
|
||||
long count = activityInstanceQuery.count();
|
||||
List<TaskDto> taskDtoList = new ArrayList<>();
|
||||
|
||||
for (HistoricActivityInstance historicActivityInstance : list) {
|
||||
String activityId = historicActivityInstance.getActivityId();
|
||||
String activityName = historicActivityInstance.getActivityName();
|
||||
String executionId = historicActivityInstance.getExecutionId();
|
||||
String taskId = historicActivityInstance.getTaskId();
|
||||
Date startTime = historicActivityInstance.getStartTime();
|
||||
Date endTime = historicActivityInstance.getEndTime();
|
||||
Long durationInMillis = historicActivityInstance.getDurationInMillis();
|
||||
String processInstanceId = historicActivityInstance.getProcessInstanceId();
|
||||
|
||||
String processDefinitionId = historicActivityInstance.getProcessDefinitionId();
|
||||
// 流程id
|
||||
String flowId = NodeUtil.getFlowId(processDefinitionId);
|
||||
|
||||
TaskDto taskDto = new TaskDto();
|
||||
taskDto.setFlowId(flowId);
|
||||
taskDto.setTaskCreateTime(startTime);
|
||||
taskDto.setTaskEndTime(endTime);
|
||||
taskDto.setNodeId(activityId);
|
||||
taskDto.setExecutionId(executionId);
|
||||
taskDto.setProcessInstanceId(processInstanceId);
|
||||
taskDto.setDurationInMillis(durationInMillis);
|
||||
taskDto.setTaskId(taskId);
|
||||
taskDto.setAssign(historicActivityInstance.getAssignee());
|
||||
taskDto.setTaskName(activityName);
|
||||
|
||||
taskDtoList.add(taskDto);
|
||||
}
|
||||
|
||||
Page<TaskDto> pageResultDto = new Page<>();
|
||||
pageResultDto.setTotal(count);
|
||||
pageResultDto.setRecords(taskDtoList);
|
||||
return R.ok(pageResultDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户待办任务
|
||||
* @param taskQueryParamDto 查询参数
|
||||
* @return 分页任务信息
|
||||
*/
|
||||
@PostMapping("/queryAssignTask")
|
||||
public R queryAssignTask(@RequestBody TaskQueryParamDto taskQueryParamDto) {
|
||||
|
||||
String assign = taskQueryParamDto.getAssign();
|
||||
|
||||
List<TaskDto> taskDtoList = new ArrayList<>();
|
||||
|
||||
int pageIndex = taskQueryParamDto.getPageNum() - 1;
|
||||
int pageSize = taskQueryParamDto.getPageSize();
|
||||
|
||||
Page<TaskDto> pageResultDto = new Page<>();
|
||||
TaskQuery taskQuery = taskService.createTaskQuery().taskAssignee(assign).orderByTaskCreateTime().desc();
|
||||
if (StrUtil.isNotBlank(taskQueryParamDto.getProcessName())) {
|
||||
List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery()
|
||||
.processInstanceNameLikeIgnoreCase(taskQueryParamDto.getProcessName())
|
||||
.list();
|
||||
if (CollUtil.isNotEmpty(processInstanceList)) {
|
||||
taskQuery.processInstanceIdIn(processInstanceList.stream()
|
||||
.map(ProcessInstance::getProcessDefinitionId)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
||||
if (ArrayUtil.isNotEmpty(taskQueryParamDto.getTaskTime())) {
|
||||
ZoneId zoneId = ZoneId.systemDefault();
|
||||
ZonedDateTime zonedBeforeDateTime = taskQueryParamDto.getTaskTime()[0].atZone(zoneId);
|
||||
ZonedDateTime zonedAfterDateTime = taskQueryParamDto.getTaskTime()[1].atZone(zoneId);
|
||||
Date beforeDate = Date.from(zonedBeforeDateTime.toInstant());
|
||||
Date afterDate = Date.from(zonedAfterDateTime.toInstant());
|
||||
taskQuery.taskCreatedBefore(afterDate).taskCreatedAfter(beforeDate);
|
||||
}
|
||||
|
||||
taskQuery.listPage(pageIndex * pageSize, pageSize).forEach(task -> {
|
||||
String taskId = task.getId();
|
||||
String processInstanceId = task.getProcessInstanceId();
|
||||
log.debug("(taskId) " + task.getName() + " processInstanceId={} executrionId={}", processInstanceId,
|
||||
task.getExecutionId());
|
||||
|
||||
Map<String, Object> taskServiceVariables = taskService.getVariables(taskId);
|
||||
Map<String, Object> runtimeServiceVariables = runtimeService.getVariables(processInstanceId);
|
||||
Map<String, Object> variables = runtimeService.getVariables(task.getExecutionId());
|
||||
log.debug("任务变量:{}", JSONUtil.toJsonStr(taskServiceVariables));
|
||||
log.debug("流程节点:{}", JSONUtil.toJsonStr(runtimeServiceVariables));
|
||||
log.debug("执行节点变量:{}", JSONUtil.toJsonStr(variables));
|
||||
|
||||
String taskDefinitionKey = task.getTaskDefinitionKey();
|
||||
String processDefinitionId = task.getProcessDefinitionId();
|
||||
String flowId = NodeUtil.getFlowId(processDefinitionId);
|
||||
|
||||
TaskDto taskDto = new TaskDto();
|
||||
taskDto.setFlowId(flowId);
|
||||
taskDto.setTaskCreateTime(task.getCreateTime());
|
||||
taskDto.setNodeId(taskDefinitionKey);
|
||||
taskDto.setParamMap(taskServiceVariables);
|
||||
taskDto.setProcessInstanceId(processInstanceId);
|
||||
taskDto.setTaskId(taskId);
|
||||
taskDto.setAssign(task.getAssignee());
|
||||
taskDto.setTaskName(task.getName());
|
||||
|
||||
taskDtoList.add(taskDto);
|
||||
});
|
||||
|
||||
long count = taskQuery.count();
|
||||
|
||||
log.debug("当前有" + count + " 个任务:");
|
||||
|
||||
pageResultDto.setTotal(count);
|
||||
pageResultDto.setRecords(taskDtoList);
|
||||
|
||||
return R.ok(pageResultDto);
|
||||
}
|
||||
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.pig4cloud.pigx.flow.engine.controller;
|
||||
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.flow.task.dto.IndexPageStatistics;
|
||||
import com.pig4cloud.pigx.flow.task.dto.VariableQueryParamDto;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.HistoryService;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.flowable.engine.history.HistoricActivityInstanceQuery;
|
||||
import org.flowable.task.api.TaskQuery;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 任务控制器
|
||||
*/
|
||||
@RestController
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/process-instance")
|
||||
public class EngineProcessInstanceController {
|
||||
|
||||
private final TaskService taskService;
|
||||
|
||||
private final HistoryService historyService;
|
||||
|
||||
private final RuntimeService runtimeService;
|
||||
|
||||
/**
|
||||
* 查询首页统计数量
|
||||
* @param userId 用户ID
|
||||
* @return 统计结果
|
||||
*/
|
||||
@GetMapping("querySimpleData")
|
||||
public R<IndexPageStatistics> querySimpleData(long userId) {
|
||||
TaskQuery taskQuery = taskService.createTaskQuery();
|
||||
|
||||
// 待办数量
|
||||
long pendingNum = taskQuery.taskAssignee(String.valueOf(userId)).count();
|
||||
// 已完成任务
|
||||
HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService
|
||||
.createHistoricActivityInstanceQuery();
|
||||
|
||||
long completedNum = historicActivityInstanceQuery.taskAssignee(String.valueOf(userId)).finished().count();
|
||||
|
||||
IndexPageStatistics indexPageStatistics = IndexPageStatistics.builder()
|
||||
.pendingNum(pendingNum)
|
||||
.completedNum(completedNum)
|
||||
.build();
|
||||
|
||||
return R.ok(indexPageStatistics);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询流程变量
|
||||
* @param paramDto 查询参数
|
||||
* @return 流程变量
|
||||
*/
|
||||
@PostMapping("queryVariables")
|
||||
public R queryVariables(@RequestBody VariableQueryParamDto paramDto) {
|
||||
|
||||
Map<String, Object> variables = runtimeService.getVariables(paramDto.getExecutionId());
|
||||
|
||||
return R.ok(variables);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
package com.pig4cloud.pigx.flow.engine.controller;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.flow.task.dto.TaskParamDto;
|
||||
import com.pig4cloud.pigx.flow.task.dto.TaskResultDto;
|
||||
import com.pig4cloud.pigx.flow.task.dto.VariableQueryParamDto;
|
||||
import com.pig4cloud.pigx.flow.task.utils.NodeUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.HistoryService;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.flowable.task.api.DelegationState;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.flowable.task.api.TaskQuery;
|
||||
import org.flowable.task.api.history.HistoricTaskInstance;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 任务控制器
|
||||
*/
|
||||
@RestController
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/task")
|
||||
public class EngineTaskController {
|
||||
|
||||
private final TaskService taskService;
|
||||
|
||||
private final HistoryService historyService;
|
||||
|
||||
private final RuntimeService runtimeService;
|
||||
|
||||
/**
|
||||
* 查询任务变量
|
||||
* @param paramDto 参数DTO
|
||||
* @return 变量Map
|
||||
*/
|
||||
@PostMapping("queryTaskVariables")
|
||||
public R queryTaskVariables(@RequestBody VariableQueryParamDto paramDto) {
|
||||
|
||||
List<String> keyList = paramDto.getKeyList();
|
||||
if (CollUtil.isEmpty(keyList)) {
|
||||
TaskQuery taskQuery = taskService.createTaskQuery();
|
||||
|
||||
Task task = taskQuery.taskId(paramDto.getTaskId()).singleResult();
|
||||
if (task == null) {
|
||||
return R.failed("任务不存在");
|
||||
}
|
||||
|
||||
Map<String, Object> variables = runtimeService.getVariables(task.getExecutionId());
|
||||
|
||||
return R.ok(variables);
|
||||
}
|
||||
|
||||
Map<String, Object> variables = taskService.getVariables(paramDto.getTaskId(), keyList);
|
||||
return R.ok(variables);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询任务详情
|
||||
* @param taskId 任务ID
|
||||
* @param userId 用户ID
|
||||
* @return 任务结果DTO
|
||||
*/
|
||||
@GetMapping("queryTask")
|
||||
public R queryTask(String taskId, String userId) {
|
||||
Optional<Task> task = Optional
|
||||
.ofNullable(taskService.createTaskQuery().taskId(taskId).taskAssignee(userId).singleResult());
|
||||
|
||||
if (task.isPresent()) {
|
||||
String processDefinitionId = task.get().getProcessDefinitionId();
|
||||
String taskDefinitionKey = task.get().getTaskDefinitionKey();
|
||||
DelegationState delegationState = task.get().getDelegationState();
|
||||
String processInstanceId = task.get().getProcessInstanceId();
|
||||
Object delegateVariable = taskService.getVariableLocal(taskId, "delegate");
|
||||
|
||||
String flowId = NodeUtil.getFlowId(processDefinitionId);
|
||||
Map<String, Object> variables = taskService.getVariables(taskId);
|
||||
Map<String, Object> variableAll = new HashMap<>(variables);
|
||||
|
||||
TaskResultDto taskResultDto = new TaskResultDto();
|
||||
taskResultDto.setFlowId(flowId);
|
||||
taskResultDto.setProcessInstanceId(processDefinitionId);
|
||||
taskResultDto.setNodeId(taskDefinitionKey);
|
||||
taskResultDto.setCurrentTask(true);
|
||||
taskResultDto.setDelegate(Convert.toBool(delegateVariable, false));
|
||||
taskResultDto.setVariableAll(variableAll);
|
||||
taskResultDto.setProcessInstanceId(processInstanceId);
|
||||
taskResultDto.setDelegationState(delegationState == null ? null : delegationState.toString());
|
||||
|
||||
return R.ok(taskResultDto);
|
||||
}
|
||||
else {
|
||||
Optional<HistoricTaskInstance> historicTaskInstance = Optional
|
||||
.ofNullable(historyService.createHistoricTaskInstanceQuery()
|
||||
.taskId(taskId)
|
||||
.taskAssignee(userId)
|
||||
.singleResult());
|
||||
|
||||
if (historicTaskInstance.isPresent()) {
|
||||
String processDefinitionId = historicTaskInstance.get().getProcessDefinitionId();
|
||||
String taskDefinitionKey = historicTaskInstance.get().getTaskDefinitionKey();
|
||||
String processInstanceId = historicTaskInstance.get().getProcessInstanceId();
|
||||
|
||||
String flowId = NodeUtil.getFlowId(processDefinitionId);
|
||||
Map<String, Object> variableAll = new HashMap<>();
|
||||
|
||||
TaskResultDto taskResultDto = new TaskResultDto();
|
||||
taskResultDto.setFlowId(flowId);
|
||||
taskResultDto.setNodeId(taskDefinitionKey);
|
||||
taskResultDto.setCurrentTask(false);
|
||||
taskResultDto.setVariableAll(variableAll);
|
||||
taskResultDto.setProcessInstanceId(processInstanceId);
|
||||
|
||||
return R.ok(taskResultDto);
|
||||
}
|
||||
else {
|
||||
return R.failed("任务不存在");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成任务
|
||||
* @param taskParamDto 任务参数DTO
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PostMapping("/complete")
|
||||
public R complete(@RequestBody TaskParamDto taskParamDto) {
|
||||
Map<String, Object> taskLocalParamMap = taskParamDto.getTaskLocalParamMap();
|
||||
if (CollUtil.isNotEmpty(taskLocalParamMap)) {
|
||||
taskService.setVariablesLocal(taskParamDto.getTaskId(), taskLocalParamMap);
|
||||
}
|
||||
|
||||
taskService.complete(taskParamDto.getTaskId(), taskParamDto.getParamMap());
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
}
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
package com.pig4cloud.pigx.flow.engine.expression;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.EscapeUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.googlecode.aviator.AviatorEvaluator;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysUser;
|
||||
import com.pig4cloud.pigx.admin.api.feign.RemoteUserService;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.flow.task.constant.NodeUserTypeEnum;
|
||||
import com.pig4cloud.pigx.flow.task.dto.NodeUser;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 表达式解析
|
||||
*/
|
||||
@Component("expressionHandler")
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ExpressionHandler {
|
||||
|
||||
private final RemoteUserService remoteUserService;
|
||||
|
||||
public Long getUserId(String key, DelegateExecution execution) {
|
||||
Object variable = execution.getVariable(key);
|
||||
NodeUser nodeUserDto = JSON.parseArray(JSON.toJSONString(variable), NodeUser.class).get(0);
|
||||
return nodeUserDto.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期时间比较
|
||||
* @param key
|
||||
* @param symbol
|
||||
* @param param
|
||||
* @param execution
|
||||
* @param format 时间格式化模式
|
||||
* @return
|
||||
*/
|
||||
public boolean dateTimeCompare(String key, String symbol, Object param, DelegateExecution execution,
|
||||
String format) {
|
||||
|
||||
Object value = execution.getVariable(key);
|
||||
|
||||
log.debug("表单值:key={} value={}", key, JSON.toJSONString(value));
|
||||
log.debug("条件 标识:{} 参数:{} 格式:{}", symbol, JSON.toJSONString(param), format);
|
||||
|
||||
// 表单值为空
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long valueTime = DateUtil.parse(value.toString(), format).getTime();
|
||||
long paramTime = DateUtil.parse(param.toString(), format).getTime();
|
||||
|
||||
return compare(StrUtil.format("${key{}{}}", symbol, paramTime), Dict.create().set("key", valueTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字类型比较
|
||||
* @param key 表单key
|
||||
* @param symbol 比较符号
|
||||
* @param param 表单参数
|
||||
* @param execution
|
||||
* @return
|
||||
*/
|
||||
public boolean numberCompare(String key, String symbol, Object param, DelegateExecution execution) {
|
||||
|
||||
Object value = execution.getVariable(key);
|
||||
|
||||
log.debug("表单值:key={} value={}", key, JSON.toJSONString(value));
|
||||
log.debug("条件 标识:{} 参数:{}", symbol, JSON.toJSONString(param));
|
||||
|
||||
// 表单值为空
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return compare(StrUtil.format("${key{}{}}", symbol, param), Dict.create().set("key", Convert.toNumber(value)));
|
||||
|
||||
}
|
||||
|
||||
private Boolean compare(String symbol, Dict value) {
|
||||
Object result = AviatorEvaluator.getInstance().execute(symbol, value);
|
||||
// 渲染结果
|
||||
log.debug("验证结果:{}", result);
|
||||
return Convert.toBool(result, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期类型对比
|
||||
* @param key 表单key
|
||||
* @param symbol 比较符号
|
||||
* @param param 比较参数值
|
||||
* @param execution 上下午执行前
|
||||
* @param format 日期格式化字符串
|
||||
* @return
|
||||
*/
|
||||
public boolean dateCompare(String key, String symbol, Object param, DelegateExecution execution, String format) {
|
||||
|
||||
Object value = execution.getVariable(key);
|
||||
|
||||
log.debug("表单值:key={} value={}", key, JSON.toJSONString(value));
|
||||
log.debug("条件 标识:{} 参数:{}", symbol, JSON.toJSONString(param));
|
||||
|
||||
// 表单值为空
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 处理表单值
|
||||
DateTime valueDateTime = DateUtil.parse(value.toString(), format);
|
||||
log.debug("表单值:{} 格式化显示:{}", valueDateTime.getTime(), DateUtil.formatDateTime(valueDateTime));
|
||||
// 处理参数值
|
||||
DateTime paramDateTime = DateUtil.parse(param.toString(), format);
|
||||
log.debug("参数值:{} 格式化显示:{}", paramDateTime.getTime(), DateUtil.formatDateTime(paramDateTime));
|
||||
|
||||
// 获取模板
|
||||
return compare(StrUtil.format("${key{}{}}", symbol, paramDateTime.getTime()),
|
||||
Dict.create().set("key", valueDateTime.getTime()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断数字数组包含
|
||||
* @param key 表单key
|
||||
* @param array 条件值
|
||||
* @return
|
||||
*/
|
||||
public boolean numberContain(String key, DelegateExecution execution, Object... array) {
|
||||
|
||||
Object value = execution.getVariable(key);
|
||||
|
||||
log.debug("表单值:key={} value={}", key, JSON.toJSONString(value));
|
||||
log.debug("条件 参数:{}", JSON.toJSONString(array));
|
||||
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return numberContain(value, array);
|
||||
}
|
||||
|
||||
private static boolean numberContain(Object value, Object[] array) {
|
||||
BigDecimal valueBigDecimal = Convert.toBigDecimal(value);
|
||||
|
||||
for (Object aLong : array) {
|
||||
if (valueBigDecimal.compareTo(Convert.toBigDecimal(aLong)) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符数组包含
|
||||
* @param key 表单key
|
||||
* @param array 条件值
|
||||
* @return
|
||||
*/
|
||||
public boolean stringContain(String key, DelegateExecution execution, String... array) {
|
||||
Object value = execution.getVariable(key);
|
||||
|
||||
log.debug("表单值:key={} value={}", key, JSON.toJSONString(value));
|
||||
log.debug("条件 参数:{}", JSON.toJSONString(array));
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
return ArrayUtil.contains(array, value.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串判断相等
|
||||
* @param key 表单key
|
||||
* @param param 参数
|
||||
* @return
|
||||
*/
|
||||
public boolean stringEqual(String key, String param, DelegateExecution execution) {
|
||||
Object value = execution.getVariable(key);
|
||||
|
||||
log.debug("表单值:key={} value={}", key, JSON.toJSONString(value));
|
||||
log.debug("条件 参数:{}", JSON.toJSONString(param));
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
return StrUtil.equals(value.toString(), param);
|
||||
}
|
||||
|
||||
public boolean deptCompare(String key, String param, String symbol, DelegateExecution execution) {
|
||||
param = EscapeUtil.unescape(param);
|
||||
|
||||
Object value = execution.getVariable(key);
|
||||
|
||||
String jsonString = JSON.toJSONString(value);
|
||||
log.debug("表单值:key={} value={} symbol={}", key, jsonString, symbol);
|
||||
log.debug("条件 参数:{}", param);
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 表单值
|
||||
List<NodeUser> nodeUserDtoList = JSON.parseArray(jsonString, NodeUser.class);
|
||||
if (CollUtil.isEmpty(nodeUserDtoList) || nodeUserDtoList.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
NodeUser nodeUserDto = nodeUserDtoList.get(0);
|
||||
|
||||
// 参数
|
||||
List<NodeUser> paramDeptList = JSON.parseArray(param, NodeUser.class);
|
||||
Long deptId = nodeUserDto.getId();
|
||||
List<Long> deptIdList = paramDeptList.stream().map(NodeUser::getId).collect(Collectors.toList());
|
||||
|
||||
return inCompare(symbol, deptId, deptIdList);
|
||||
|
||||
}
|
||||
|
||||
private static boolean inCompare(String symbol, Long deptId, List<Long> deptIdList) {
|
||||
if (StrUtil.equalsAny(symbol, "in", "==")) {
|
||||
// 属于
|
||||
return deptIdList.contains(deptId);
|
||||
}
|
||||
if (StrUtil.equalsAny(symbol, "notin", "!=")) {
|
||||
// 属于
|
||||
return !deptIdList.contains(deptId);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* user判断
|
||||
* @param key 表单key
|
||||
* @param param 参数
|
||||
* @return
|
||||
*/
|
||||
public boolean userCompare(String key, String param, String symbol, DelegateExecution execution) {
|
||||
param = EscapeUtil.unescape(param);
|
||||
|
||||
Object value = execution.getVariable(key);
|
||||
|
||||
String jsonString = JSON.toJSONString(value);
|
||||
log.debug("表单值:key={} value={} symbol={} ", key, jsonString, symbol);
|
||||
log.debug("条件 参数:{}", param);
|
||||
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 表单值
|
||||
List<NodeUser> nodeUserDtoList = JSON.parseArray(jsonString, NodeUser.class);
|
||||
|
||||
if (CollUtil.isEmpty(nodeUserDtoList) || nodeUserDtoList.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NodeUser nodeUserDto = nodeUserDtoList.get(0);
|
||||
|
||||
// 参数
|
||||
List<NodeUser> paramDeptList = JSON.parseArray(param, NodeUser.class);
|
||||
|
||||
List<Long> deptIdList = paramDeptList.stream()
|
||||
.filter(w -> StrUtil.equals(w.getType(), NodeUserTypeEnum.DEPT.getKey()))
|
||||
.map(NodeUser::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Long> userIdList = paramDeptList.stream()
|
||||
.filter(w -> StrUtil.equals(w.getType(), NodeUserTypeEnum.USER.getKey()))
|
||||
.map(NodeUser::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (CollUtil.isNotEmpty(deptIdList)) {
|
||||
R<List<SysUser>> r = remoteUserService.getUserIdListByDeptIdList(deptIdList);
|
||||
List<Long> data = r.getData().stream().map(SysUser::getUserId).toList();
|
||||
userIdList.addAll(data);
|
||||
}
|
||||
|
||||
return inCompare(symbol, Convert.toLong(nodeUserDto.getId()), userIdList);
|
||||
}
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.pig4cloud.pigx.flow.engine.expression.condition;
|
||||
|
||||
import com.pig4cloud.pigx.flow.task.dto.Condition;
|
||||
|
||||
/**
|
||||
* 节点单个条件处理器
|
||||
*/
|
||||
public interface NodeConditionStrategy {
|
||||
|
||||
/**
|
||||
* 抽象方法 处理表达式
|
||||
*/
|
||||
String handle(Condition condition);
|
||||
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package com.pig4cloud.pigx.flow.engine.expression.condition;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Condition;
|
||||
import com.pig4cloud.pigx.flow.task.dto.GroupCondition;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Node;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class NodeExpressionStrategyFactory {
|
||||
|
||||
public static String handleSingleCondition(Condition nodeConditionDto) {
|
||||
Map<String, NodeConditionStrategy> nodeConditionStrategyMap = SpringUtil
|
||||
.getBeansOfType(NodeConditionStrategy.class);
|
||||
NodeConditionStrategy nodeConditionHandler = nodeConditionStrategyMap
|
||||
.get(nodeConditionDto.getKeyType() + "NodeConditionStrategy");
|
||||
if (nodeConditionHandler == null) {
|
||||
return "(1==1)";
|
||||
}
|
||||
return nodeConditionHandler.handle(nodeConditionDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 组内处理表达式
|
||||
* @param groupDto
|
||||
* @return
|
||||
*/
|
||||
public static String handleGroupCondition(GroupCondition groupDto) {
|
||||
|
||||
List<String> exps = new ArrayList<>();
|
||||
|
||||
for (Condition condition : groupDto.getConditionList()) {
|
||||
String singleExpression = handleSingleCondition(condition);
|
||||
exps.add(singleExpression);
|
||||
}
|
||||
Boolean mode = groupDto.getMode();
|
||||
|
||||
if (!mode) {
|
||||
String join = CollUtil.join(exps, "||");
|
||||
|
||||
return "(" + join + ")";
|
||||
}
|
||||
|
||||
String join = CollUtil.join(exps, "&&");
|
||||
return "(" + join + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个分支表达式
|
||||
* @return
|
||||
*/
|
||||
public static String handle(Node node) {
|
||||
|
||||
List<String> exps = new ArrayList<>();
|
||||
|
||||
List<GroupCondition> groups = node.getConditionList();
|
||||
if (CollUtil.isEmpty(groups)) {
|
||||
return "${1==1}";
|
||||
}
|
||||
for (GroupCondition group : groups) {
|
||||
String s = handleGroupCondition(group);
|
||||
exps.add(s);
|
||||
}
|
||||
|
||||
if (!node.getGroupMode()) {
|
||||
String join = CollUtil.join(exps, "||");
|
||||
return "${(" + join + ")}";
|
||||
}
|
||||
|
||||
String join = CollUtil.join(exps, "&&");
|
||||
return "${(" + join + ")}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理默认分支表达式
|
||||
* @param branchs 所有分支
|
||||
* @return
|
||||
*/
|
||||
public static String handleDefaultBranch(List<Node> branchs) {
|
||||
|
||||
List<String> expList = new ArrayList<>();
|
||||
|
||||
int size = branchs.size();
|
||||
|
||||
int index = 1;
|
||||
for (Node branch : branchs) {
|
||||
|
||||
if (index == size) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String exp = handle(branch);
|
||||
String s = StrUtil.subBetween(exp, "${", "}");
|
||||
expList.add(StrUtil.format("({})", s));
|
||||
|
||||
index++;
|
||||
}
|
||||
String join = StrUtil.format("!({})", CollUtil.join(expList, "||"));
|
||||
return "${" + join + "}";
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.pig4cloud.pigx.flow.engine.expression.condition.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Condition;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 字符类型处理器
|
||||
*/
|
||||
@Component("DateNodeConditionStrategy")
|
||||
public class DateNodeConditionStrategy implements NodeConditionStrategy {
|
||||
|
||||
/**
|
||||
* 抽象方法 处理表达式
|
||||
*/
|
||||
@Override
|
||||
public String handle(Condition condition) {
|
||||
|
||||
String compare = condition.getExpression();
|
||||
String id = condition.getKey();
|
||||
Object value = condition.getValue();
|
||||
|
||||
return StrUtil.format("(expressionHandler.dateTimeCompare(\"{}\",\"{}\",\"{}\",execution,\"yyyy-MM-dd\"))", id,
|
||||
compare, value);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.pig4cloud.pigx.flow.engine.expression.condition.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Condition;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 字符类型处理器
|
||||
*/
|
||||
@Component("DateTimeNodeConditionStrategy")
|
||||
public class DateTimeNodeConditionStrategy implements NodeConditionStrategy {
|
||||
|
||||
/**
|
||||
* 抽象方法 处理表达式
|
||||
*/
|
||||
@Override
|
||||
public String handle(Condition condition) {
|
||||
|
||||
String compare = condition.getExpression();
|
||||
String id = condition.getKey();
|
||||
Object value = condition.getValue();
|
||||
|
||||
return StrUtil.format(
|
||||
"(expressionHandler.dateTimeCompare(\"{}\",\"{}\",\"{}\",execution,\"yyyy-MM-dd " + "HH:mm:ss\"))", id,
|
||||
compare, value);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.pig4cloud.pigx.flow.engine.expression.condition.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Condition;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 字符类型处理器
|
||||
*/
|
||||
@Component("InputNodeConditionStrategy")
|
||||
public class InputNodeConditionStrategy implements NodeConditionStrategy {
|
||||
|
||||
/**
|
||||
* 抽象方法 处理表达式
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public String handle(Condition condition) {
|
||||
|
||||
String compare = condition.getExpression();
|
||||
String id = condition.getKey();
|
||||
Object value = condition.getValue();
|
||||
if (StrUtil.equals(compare, "==")) {
|
||||
return StrUtil.format("(expressionHandler.stringEqual(\"{}\",\"{}\",execution))", id, value);
|
||||
}
|
||||
if (StrUtil.equals(compare, "!=")) {
|
||||
return StrUtil.format("(!expressionHandler.stringEqual(\"{}\",\"{}\",execution))", id, value);
|
||||
}
|
||||
return "(2==2)";
|
||||
}
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.pig4cloud.pigx.flow.engine.expression.condition.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Condition;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 字符类型处理器
|
||||
*/
|
||||
@Component("MoneyNodeConditionStrategy")
|
||||
public class MoneyNodeConditionStrategy implements NodeConditionStrategy {
|
||||
|
||||
/**
|
||||
* 抽象方法 处理表达式
|
||||
*/
|
||||
@Override
|
||||
public String handle(Condition condition) {
|
||||
|
||||
String compare = condition.getExpression();
|
||||
String id = condition.getKey();
|
||||
Object value = condition.getValue();
|
||||
|
||||
return StrUtil.format("(expressionHandler.numberCompare(\"{}\",\"{}\",{},execution))", id, compare, value);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.pig4cloud.pigx.flow.engine.expression.condition.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Condition;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 字符类型处理器
|
||||
*/
|
||||
@Component("NumberNodeConditionStrategy")
|
||||
public class NumberNodeConditionStrategy implements NodeConditionStrategy {
|
||||
|
||||
/**
|
||||
* 抽象方法 处理表达式
|
||||
*/
|
||||
@Override
|
||||
public String handle(Condition condition) {
|
||||
|
||||
String compare = condition.getExpression();
|
||||
String id = condition.getKey();
|
||||
Object value = condition.getValue();
|
||||
|
||||
return StrUtil.format("(expressionHandler.numberCompare(\"{}\",\"{}\",{},execution))", id, compare, value);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.pig4cloud.pigx.flow.engine.expression.condition.impl;
|
||||
|
||||
import cn.hutool.core.util.EscapeUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Condition;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 字符类型处理器
|
||||
*/
|
||||
@Component("SelectDeptNodeConditionStrategy")
|
||||
public class SelectDeptNodeConditionStrategy implements NodeConditionStrategy {
|
||||
|
||||
/**
|
||||
* 抽象方法 处理表达式
|
||||
*/
|
||||
@Override
|
||||
public String handle(Condition condition) {
|
||||
|
||||
String compare = condition.getExpression();
|
||||
String id = condition.getKey();
|
||||
Object value = condition.getValue();
|
||||
|
||||
return StrUtil.format("(expressionHandler.deptCompare(\"{}\",\"{}\",\"{}\", execution))", id,
|
||||
EscapeUtil.escape(JSON.toJSONString(value)), compare);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.pig4cloud.pigx.flow.engine.expression.condition.impl;
|
||||
|
||||
import cn.hutool.core.util.EscapeUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Condition;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 字符类型处理器
|
||||
*/
|
||||
@Component("SelectUserNodeConditionStrategy")
|
||||
public class SelectUserNodeConditionStrategy implements NodeConditionStrategy {
|
||||
|
||||
/**
|
||||
* 抽象方法 处理表达式
|
||||
*/
|
||||
@Override
|
||||
public String handle(Condition condition) {
|
||||
|
||||
String compare = condition.getExpression();
|
||||
String id = condition.getKey();
|
||||
Object value = condition.getValue();
|
||||
|
||||
return StrUtil.format("(expressionHandler.userCompare(\"{}\",\"{}\",\"{}\", execution))", id,
|
||||
EscapeUtil.escape(JSON.toJSONString(value)), compare);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.pig4cloud.pigx.flow.engine.expression.condition.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Condition;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 字符类型处理器
|
||||
*/
|
||||
@Component("SingleSelectNodeConditionStrategy")
|
||||
public class SingleSelectNodeConditionStrategy implements NodeConditionStrategy {
|
||||
|
||||
/**
|
||||
* 抽象方法 处理表达式
|
||||
*/
|
||||
@Override
|
||||
public String handle(Condition condition) {
|
||||
|
||||
String compare = condition.getExpression();
|
||||
String id = condition.getKey();
|
||||
Object value = condition.getValue();
|
||||
|
||||
List<?> list = Convert.toList(value);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (Object o : list) {
|
||||
sb.append(",\"").append(o.toString()).append("\"");
|
||||
}
|
||||
String string = sb.toString();
|
||||
if (CollUtil.isNotEmpty(list)) {
|
||||
string = string.substring(1);
|
||||
}
|
||||
if (compare.equals("in")) {
|
||||
return StrUtil.format("(expressionHandler.stringContain(\"{}\", execution,{}))", id, string);
|
||||
}
|
||||
|
||||
return StrUtil.format("(!expressionHandler.stringContain(\"{}\", execution,{}))", id, string);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.pig4cloud.pigx.flow.engine.expression.condition.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Condition;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 字符类型处理器
|
||||
*/
|
||||
@Component("TextareaNodeConditionStrategy")
|
||||
public class TextareaNodeConditionStrategy implements NodeConditionStrategy {
|
||||
|
||||
/**
|
||||
* 抽象方法 处理表达式
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public String handle(Condition condition) {
|
||||
|
||||
String compare = condition.getExpression();
|
||||
String id = condition.getKey();
|
||||
Object value = condition.getValue();
|
||||
if (StrUtil.equals(compare, "==")) {
|
||||
return StrUtil.format("(expressionHandler.stringEqual(\"{}\",\"{}\",execution))", id, value);
|
||||
}
|
||||
if (StrUtil.equals(compare, "!=")) {
|
||||
return StrUtil.format("(!expressionHandler.stringEqual(\"{}\",\"{}\",execution))", id, value);
|
||||
}
|
||||
return "(2==2)";
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.pig4cloud.pigx.flow.engine.expression.condition.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Condition;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 字符类型处理器
|
||||
*/
|
||||
@Component("TimeNodeConditionStrategy")
|
||||
public class TimeNodeConditionStrategy implements NodeConditionStrategy {
|
||||
|
||||
/**
|
||||
* 抽象方法 处理表达式
|
||||
*/
|
||||
@Override
|
||||
public String handle(Condition condition) {
|
||||
|
||||
String compare = condition.getExpression();
|
||||
String id = condition.getKey();
|
||||
Object value = condition.getValue();
|
||||
|
||||
return StrUtil.format("(expressionHandler.dateTimeCompare(\"{}\",\"{}\",\"{}\",execution,\"HH:mm:ss\"))", id,
|
||||
compare, value);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package com.pig4cloud.pigx.flow.engine.listeners;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.flow.task.api.feign.RemoteFlowTaskService;
|
||||
import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Nobody;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Node;
|
||||
import com.pig4cloud.pigx.flow.task.dto.NodeUser;
|
||||
import com.pig4cloud.pigx.flow.task.utils.NodeUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.flowable.task.service.delegate.DelegateTask;
|
||||
import org.flowable.task.service.delegate.TaskListener;
|
||||
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
|
||||
|
||||
/**
|
||||
* 审批创建监听器
|
||||
*/
|
||||
@Slf4j
|
||||
public class ApprovalCreateListener implements TaskListener {
|
||||
|
||||
/**
|
||||
* 当任务被触发时,执行该方法
|
||||
* @param delegateTask 委派的任务对象
|
||||
*/
|
||||
@Override
|
||||
public void notify(DelegateTask delegateTask) {
|
||||
log.debug(delegateTask.getClass().getCanonicalName());
|
||||
TaskService taskService = SpringUtil.getBean(TaskService.class);
|
||||
|
||||
String assignee = delegateTask.getAssignee();
|
||||
String name = delegateTask.getName();
|
||||
log.debug("任务{}-执行人:{}", name, assignee);
|
||||
TaskEntityImpl taskEntity = (TaskEntityImpl) delegateTask;
|
||||
String nodeId = taskEntity.getTaskDefinitionKey();
|
||||
String processDefinitionId = taskEntity.getProcessDefinitionId();
|
||||
|
||||
// 获取流程id
|
||||
String flowId = NodeUtil.getFlowId(processDefinitionId);
|
||||
|
||||
if (StrUtil.isBlank(assignee)
|
||||
|| StrUtil.equals(ProcessInstanceConstant.DEFAULT_EMPTY_ASSIGN.toString(), assignee)) {
|
||||
|
||||
RemoteFlowTaskService remoteFlowTaskService = SpringUtil.getBean(RemoteFlowTaskService.class);
|
||||
|
||||
// 查询节点原始数据
|
||||
Node node = remoteFlowTaskService.queryNodeOriData(flowId, nodeId).getData();
|
||||
|
||||
Nobody nobody = node.getNobody();
|
||||
|
||||
String handler = nobody.getHandler();
|
||||
|
||||
if (StrUtil.equals(handler, ProcessInstanceConstant.USER_TASK_NOBODY_HANDLER_TO_PASS)) {
|
||||
// 直接通过
|
||||
Dict param = Dict.create().set(StrUtil.format("{}_approve_condition", nodeId), true);
|
||||
taskService.complete(taskEntity.getId(), param);
|
||||
}
|
||||
|
||||
if (StrUtil.equals(handler, ProcessInstanceConstant.USER_TASK_NOBODY_HANDLER_TO_ADMIN)) {
|
||||
// 指派给管理员
|
||||
|
||||
R<Long> longR = remoteFlowTaskService.queryProcessAdmin(flowId);
|
||||
|
||||
Long adminId = longR.getData();
|
||||
|
||||
taskService.setAssignee(taskEntity.getId(), String.valueOf(adminId));
|
||||
}
|
||||
|
||||
if (StrUtil.equals(handler, ProcessInstanceConstant.USER_TASK_NOBODY_HANDLER_TO_USER)) {
|
||||
// 指定用户
|
||||
|
||||
NodeUser nodeUser = nobody.getAssignedUser().get(0);
|
||||
|
||||
taskService.setAssignee(taskEntity.getId(), nodeUser.getId().toString());
|
||||
}
|
||||
|
||||
if (StrUtil.equals(handler, ProcessInstanceConstant.USER_TASK_NOBODY_HANDLER_TO_END)) {
|
||||
// 结束
|
||||
Dict param = Dict.create().set(StrUtil.format("{}_approve_condition", nodeId), false);
|
||||
taskService.complete(taskEntity.getId(), param);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
package com.pig4cloud.pigx.flow.engine.listeners;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.pig4cloud.pigx.flow.task.api.feign.RemoteFlowTaskService;
|
||||
import com.pig4cloud.pigx.flow.task.dto.*;
|
||||
import com.pig4cloud.pigx.flow.task.utils.NodeUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEntityEvent;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.event.impl.FlowableActivityEventImpl;
|
||||
import org.flowable.engine.delegate.event.impl.FlowableMultiInstanceActivityCompletedEventImpl;
|
||||
import org.flowable.engine.delegate.event.impl.FlowableProcessStartedEventImpl;
|
||||
import org.flowable.engine.delegate.event.impl.FlowableProcessTerminatedEventImpl;
|
||||
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
|
||||
import org.flowable.task.api.DelegationState;
|
||||
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
|
||||
import org.flowable.variable.api.event.FlowableVariableEvent;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 流程监听器
|
||||
*/
|
||||
@Slf4j
|
||||
public class FlowProcessEventListener implements FlowableEventListener {
|
||||
|
||||
/**
|
||||
* 当事件被触发时调用
|
||||
* @param event 事件对象
|
||||
*/
|
||||
@Override
|
||||
public void onEvent(FlowableEvent event) {
|
||||
RemoteFlowTaskService remoteFlowTaskService = SpringUtil.getBean(RemoteFlowTaskService.class);
|
||||
|
||||
log.info("分支监听器 类型={} class={}", event.getType(), event.getClass().getCanonicalName());
|
||||
if (event.getType().toString().equals(FlowableEngineEventType.ACTIVITY_STARTED.toString())) {
|
||||
// 节点开始执行
|
||||
FlowableActivityEventImpl flowableActivityEvent = (FlowableActivityEventImpl) event;
|
||||
String activityId = flowableActivityEvent.getActivityId();
|
||||
String activityName = flowableActivityEvent.getActivityName();
|
||||
log.info("节点id:{} 名字:{}", activityId, activityName);
|
||||
|
||||
String processInstanceId = flowableActivityEvent.getProcessInstanceId();
|
||||
|
||||
String processDefinitionId = flowableActivityEvent.getProcessDefinitionId();
|
||||
String flowId = NodeUtil.getFlowId(processDefinitionId);
|
||||
|
||||
Node node = remoteFlowTaskService.queryNodeOriData(flowId, activityId).getData();
|
||||
|
||||
ProcessNodeRecordParamDto processNodeRecordParamDto = new ProcessNodeRecordParamDto();
|
||||
processNodeRecordParamDto.setFlowId(flowId);
|
||||
processNodeRecordParamDto.setProcessInstanceId(processInstanceId);
|
||||
processNodeRecordParamDto.setNodeId(activityId);
|
||||
if (node != null) {
|
||||
processNodeRecordParamDto.setNodeType(String.valueOf(node.getType()));
|
||||
}
|
||||
processNodeRecordParamDto.setNodeName(activityName);
|
||||
processNodeRecordParamDto.setExecutionId(flowableActivityEvent.getExecutionId());
|
||||
remoteFlowTaskService.startNodeEvent(processNodeRecordParamDto);
|
||||
}
|
||||
|
||||
if (event.getType()
|
||||
.toString()
|
||||
.equals(FlowableEngineEventType.MULTI_INSTANCE_ACTIVITY_COMPLETED_WITH_CONDITION.toString())
|
||||
|| event.getType()
|
||||
.toString()
|
||||
.equals(FlowableEngineEventType.MULTI_INSTANCE_ACTIVITY_COMPLETED.toString())) {
|
||||
// 多实例任务
|
||||
FlowableMultiInstanceActivityCompletedEventImpl flowableActivityEvent = (FlowableMultiInstanceActivityCompletedEventImpl) event;
|
||||
String activityId = flowableActivityEvent.getActivityId();
|
||||
String activityName = flowableActivityEvent.getActivityName();
|
||||
log.info("节点id:{} 名字:{}", activityId, activityName);
|
||||
|
||||
String processInstanceId = flowableActivityEvent.getProcessInstanceId();
|
||||
|
||||
String processDefinitionId = flowableActivityEvent.getProcessDefinitionId();
|
||||
String flowId = NodeUtil.getFlowId(processDefinitionId);
|
||||
|
||||
ProcessNodeRecordParamDto processNodeRecordParamDto = new ProcessNodeRecordParamDto();
|
||||
processNodeRecordParamDto.setFlowId(flowId);
|
||||
processNodeRecordParamDto.setExecutionId(flowableActivityEvent.getExecutionId());
|
||||
processNodeRecordParamDto.setProcessInstanceId(processInstanceId);
|
||||
// processNodeRecordParamDto.setData(JSON.toJSONString(processVariables));
|
||||
processNodeRecordParamDto.setNodeId(activityId);
|
||||
// processNodeRecordParamDto.setNodeType(nodeDto.getType());
|
||||
processNodeRecordParamDto.setNodeName(activityName);
|
||||
|
||||
remoteFlowTaskService.endNodeEvent(processNodeRecordParamDto);
|
||||
}
|
||||
if (event.getType().toString().equals(FlowableEngineEventType.ACTIVITY_COMPLETED.toString())) {
|
||||
// 节点完成执行
|
||||
|
||||
FlowableActivityEventImpl flowableActivityEvent = (FlowableActivityEventImpl) event;
|
||||
String activityId = flowableActivityEvent.getActivityId();
|
||||
String activityName = flowableActivityEvent.getActivityName();
|
||||
log.info("节点id:{} 名字:{}", activityId, activityName);
|
||||
|
||||
String processInstanceId = flowableActivityEvent.getProcessInstanceId();
|
||||
|
||||
String processDefinitionId = flowableActivityEvent.getProcessDefinitionId();
|
||||
String flowId = NodeUtil.getFlowId(processDefinitionId);
|
||||
|
||||
ProcessNodeRecordParamDto processNodeRecordParamDto = new ProcessNodeRecordParamDto();
|
||||
processNodeRecordParamDto.setFlowId(flowId);
|
||||
processNodeRecordParamDto.setExecutionId(flowableActivityEvent.getExecutionId());
|
||||
processNodeRecordParamDto.setProcessInstanceId(processInstanceId);
|
||||
// processNodeRecordParamDto.setData(JSON.toJSONString(processVariables));
|
||||
processNodeRecordParamDto.setNodeId(activityId);
|
||||
// processNodeRecordParamDto.setNodeType(nodeDto.getType());
|
||||
processNodeRecordParamDto.setNodeName(activityName);
|
||||
|
||||
remoteFlowTaskService.endNodeEvent(processNodeRecordParamDto);
|
||||
|
||||
}
|
||||
|
||||
if (event.getType().toString().equals(FlowableEngineEventType.VARIABLE_UPDATED.toString())) {
|
||||
// 变量变化了
|
||||
FlowableVariableEvent flowableVariableEvent = (FlowableVariableEvent) event;
|
||||
log.debug("变量[{}]变化了:{} ", flowableVariableEvent.getVariableName(),
|
||||
flowableVariableEvent.getVariableValue());
|
||||
}
|
||||
|
||||
if (event.getType().toString().equals(FlowableEngineEventType.VARIABLE_CREATED.toString())) {
|
||||
// 变量创建了
|
||||
FlowableVariableEvent flowableVariableEvent = (FlowableVariableEvent) event;
|
||||
log.debug("变量[{}]创建了:{} ", flowableVariableEvent.getVariableName(),
|
||||
flowableVariableEvent.getVariableValue());
|
||||
}
|
||||
if (event.getType().toString().equals(FlowableEngineEventType.VARIABLE_DELETED.toString())) {
|
||||
// 变量删除了
|
||||
FlowableVariableEvent flowableVariableEvent = (FlowableVariableEvent) event;
|
||||
log.debug("变量[{}]删除了:{} ", flowableVariableEvent.getVariableName(),
|
||||
flowableVariableEvent.getVariableValue());
|
||||
}
|
||||
if (event.getType()
|
||||
.toString()
|
||||
.equals(FlowableEngineEventType.PROCESS_COMPLETED_WITH_TERMINATE_END_EVENT.toString())) {
|
||||
// 流程开完成
|
||||
FlowableProcessTerminatedEventImpl e = (FlowableProcessTerminatedEventImpl) event;
|
||||
DelegateExecution execution = e.getExecution();
|
||||
String processInstanceId = e.getProcessInstanceId();
|
||||
ExecutionEntityImpl entity = (ExecutionEntityImpl) e.getEntity();
|
||||
|
||||
ProcessInstanceParamDto processInstanceParamDto = new ProcessInstanceParamDto();
|
||||
processInstanceParamDto.setProcessInstanceId(processInstanceId);
|
||||
remoteFlowTaskService.endProcessEvent(processInstanceParamDto);
|
||||
|
||||
}
|
||||
|
||||
if (event.getType().toString().equals(FlowableEngineEventType.TASK_COMPLETED.toString())) {
|
||||
|
||||
TaskService taskService = SpringUtil.getBean(TaskService.class);
|
||||
|
||||
// 任务完成
|
||||
FlowableEntityEvent flowableEntityEvent = (FlowableEntityEvent) event;
|
||||
TaskEntityImpl task = (TaskEntityImpl) flowableEntityEvent.getEntity();
|
||||
// 执行人id
|
||||
String assignee = task.getAssignee();
|
||||
|
||||
// nodeid
|
||||
String taskDefinitionKey = task.getTaskDefinitionKey();
|
||||
|
||||
// 实例id
|
||||
String processInstanceId = task.getProcessInstanceId();
|
||||
|
||||
String processDefinitionId = task.getProcessDefinitionId();
|
||||
// 流程id
|
||||
String flowId = NodeUtil.getFlowId(processDefinitionId);
|
||||
ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto = new ProcessNodeRecordAssignUserParamDto();
|
||||
processNodeRecordAssignUserParamDto.setFlowId(flowId);
|
||||
processNodeRecordAssignUserParamDto.setProcessInstanceId(processInstanceId);
|
||||
processNodeRecordAssignUserParamDto.setData(JSON.toJSONString(taskService.getVariables(task.getId())));
|
||||
processNodeRecordAssignUserParamDto
|
||||
.setLocalData(JSON.toJSONString(taskService.getVariablesLocal(task.getId())));
|
||||
processNodeRecordAssignUserParamDto.setNodeId(taskDefinitionKey);
|
||||
processNodeRecordAssignUserParamDto.setUserId(Long.parseLong(assignee));
|
||||
processNodeRecordAssignUserParamDto.setTaskId(task.getId());
|
||||
processNodeRecordAssignUserParamDto.setNodeName(task.getName());
|
||||
processNodeRecordAssignUserParamDto.setTaskType("COMPLETE");
|
||||
processNodeRecordAssignUserParamDto.setApproveDesc(Convert.toStr(task.getVariableLocal("approveDesc")));
|
||||
processNodeRecordAssignUserParamDto.setExecutionId(task.getExecutionId());
|
||||
|
||||
remoteFlowTaskService.taskEndEvent(processNodeRecordAssignUserParamDto);
|
||||
|
||||
}
|
||||
if (event.getType().toString().equals(FlowableEngineEventType.TASK_ASSIGNED.toString())) {
|
||||
// 任务被指派了人员
|
||||
FlowableEntityEvent flowableEntityEvent = (FlowableEntityEvent) event;
|
||||
TaskEntityImpl task = (TaskEntityImpl) flowableEntityEvent.getEntity();
|
||||
// 执行人id
|
||||
String assignee = task.getAssignee();
|
||||
// 任务拥有者
|
||||
String owner = task.getOwner();
|
||||
//
|
||||
String delegationStateString = task.getDelegationStateString();
|
||||
|
||||
// nodeid
|
||||
String taskDefinitionKey = task.getTaskDefinitionKey();
|
||||
|
||||
// 实例id
|
||||
String processInstanceId = task.getProcessInstanceId();
|
||||
|
||||
String processDefinitionId = task.getProcessDefinitionId();
|
||||
// 流程id
|
||||
String flowId = NodeUtil.getFlowId(processDefinitionId);
|
||||
ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto = new ProcessNodeRecordAssignUserParamDto();
|
||||
processNodeRecordAssignUserParamDto.setFlowId(flowId);
|
||||
processNodeRecordAssignUserParamDto.setProcessInstanceId(processInstanceId);
|
||||
// processNodeRecordAssignUserParamDto.setData();
|
||||
processNodeRecordAssignUserParamDto.setNodeId(taskDefinitionKey);
|
||||
processNodeRecordAssignUserParamDto.setUserId(Long.parseLong(assignee));
|
||||
processNodeRecordAssignUserParamDto.setTaskId(task.getId());
|
||||
processNodeRecordAssignUserParamDto.setNodeName(task.getName());
|
||||
processNodeRecordAssignUserParamDto
|
||||
.setTaskType(StrUtil.equals(DelegationState.PENDING.toString(), delegationStateString) ? "DELEGATION"
|
||||
: (StrUtil.equals(DelegationState.RESOLVED.toString(), delegationStateString) ? "RESOLVED"
|
||||
: ""));
|
||||
processNodeRecordAssignUserParamDto.setApproveDesc(Convert.toStr(task.getVariableLocal("approveDesc")));
|
||||
processNodeRecordAssignUserParamDto.setExecutionId(task.getExecutionId());
|
||||
|
||||
remoteFlowTaskService.startAssignUser(processNodeRecordAssignUserParamDto);
|
||||
|
||||
}
|
||||
|
||||
if (event.getType().toString().equals(FlowableEngineEventType.PROCESS_STARTED.toString())) {
|
||||
// 流程开始了
|
||||
FlowableProcessStartedEventImpl flowableProcessStartedEvent = (FlowableProcessStartedEventImpl) event;
|
||||
|
||||
ExecutionEntityImpl entity = (ExecutionEntityImpl) flowableProcessStartedEvent.getEntity();
|
||||
DelegateExecution execution = flowableProcessStartedEvent.getExecution();
|
||||
String processInstanceId = flowableProcessStartedEvent.getProcessInstanceId();
|
||||
{
|
||||
// 上级实例id
|
||||
String nestedProcessInstanceId = flowableProcessStartedEvent.getNestedProcessInstanceId();
|
||||
|
||||
String flowId = entity.getProcessDefinitionKey();
|
||||
|
||||
Object variable = execution.getVariable("root");
|
||||
Long startUserId = JSON.parseArray(JSON.toJSONString(variable), NodeUser.class).get(0).getId();
|
||||
Map<String, Object> variables = execution.getVariables();
|
||||
|
||||
ProcessInstanceRecordParamDto processInstanceRecordParamDto = new ProcessInstanceRecordParamDto();
|
||||
processInstanceRecordParamDto.setUserId(startUserId);
|
||||
processInstanceRecordParamDto.setParentProcessInstanceId(nestedProcessInstanceId);
|
||||
processInstanceRecordParamDto.setFlowId(flowId);
|
||||
processInstanceRecordParamDto.setProcessInstanceId(processInstanceId);
|
||||
processInstanceRecordParamDto.setFormData(JSON.toJSONString(variables));
|
||||
remoteFlowTaskService.createProcessEvent(processInstanceRecordParamDto);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果监听器抛出异常是否终止当前操作
|
||||
* @return 是否终止当前操作
|
||||
*/
|
||||
@Override
|
||||
public boolean isFailOnException() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回监听器是否在事务生命周期事件发生时立即触发
|
||||
* @return 是否在事务生命周期事件上触发
|
||||
*/
|
||||
@Override
|
||||
public boolean isFireOnTransactionLifecycleEvent() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果非空,表示在当前事务的生命周期中的触发点
|
||||
* @return 事务生命周期中的触发点
|
||||
*/
|
||||
@Override
|
||||
public String getOnTransaction() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.pig4cloud.pigx.flow.engine.node;
|
||||
|
||||
import com.pig4cloud.pigx.flow.task.dto.Node;
|
||||
import com.pig4cloud.pigx.flow.task.dto.NodeUser;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 指定用户策略处理器
|
||||
*/
|
||||
public interface AssignUserStrategy {
|
||||
|
||||
/**
|
||||
* 抽象方法 处理表达式
|
||||
* @param node
|
||||
* @param rootUser
|
||||
* @param variables
|
||||
*/
|
||||
List<Long> handle(Node node, NodeUser rootUser, Map<String, Object> variables);
|
||||
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package com.pig4cloud.pigx.flow.engine.node;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.pig4cloud.pigx.flow.task.api.feign.RemoteFlowTaskService;
|
||||
import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Node;
|
||||
import com.pig4cloud.pigx.flow.task.dto.NodeUser;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Component("multiInstanceHandler")
|
||||
public class MultiInstanceHandler {
|
||||
|
||||
private final RemoteFlowTaskService remoteFlowTaskService;
|
||||
|
||||
private final Map<String, AssignUserStrategy> assignUserStrategyMap;
|
||||
|
||||
/**
|
||||
* 解析执行人
|
||||
* @param execution 流程执行对象
|
||||
* @return 执行人集合
|
||||
*/
|
||||
public List<Long> resolveAssignee(DelegateExecution execution) {
|
||||
// 执行人集合
|
||||
List<Long> assignList = new ArrayList<>();
|
||||
|
||||
ExecutionEntityImpl entity = (ExecutionEntityImpl) execution;
|
||||
|
||||
String flowId = entity.getProcessDefinitionKey();
|
||||
String nodeId = entity.getActivityId();
|
||||
|
||||
log.debug("nodeId={} nodeName={}", nodeId, entity.getActivityName());
|
||||
|
||||
// 发起人
|
||||
Object rootUserObj = execution.getVariable("root");
|
||||
NodeUser rootUser = JSON.parseArray(JSON.toJSONString(rootUserObj), NodeUser.class).get(0);
|
||||
|
||||
// 节点数据
|
||||
Node node = remoteFlowTaskService.queryNodeOriData(flowId, nodeId).getData();
|
||||
if (node != null) {
|
||||
Map<String, Object> variables = execution.getVariables();
|
||||
Integer assignedType = node.getAssignedType();
|
||||
List<Long> userIdList = assignUserStrategyMap.get(assignedType + "AssignUserStrategy")
|
||||
.handle(node, rootUser, variables);
|
||||
assignList.addAll(userIdList);
|
||||
}
|
||||
else {
|
||||
// 默认值
|
||||
String format = StrUtil.format("{}_assignee_default_list", nodeId);
|
||||
Object variable = execution.getVariable(format);
|
||||
List<NodeUser> nodeUserDtos = JSON.parseArray(JSON.toJSONString(variable), NodeUser.class);
|
||||
if (CollUtil.isNotEmpty(nodeUserDtos)) {
|
||||
List<Long> collect = nodeUserDtos.stream().map(NodeUser::getId).toList();
|
||||
assignList.addAll(collect);
|
||||
}
|
||||
}
|
||||
|
||||
Optional.of(assignList).orElseGet(() -> {
|
||||
List<Long> defaultList = new ArrayList<>();
|
||||
defaultList.add(ProcessInstanceConstant.DEFAULT_EMPTY_ASSIGN);
|
||||
return defaultList;
|
||||
});
|
||||
|
||||
return assignList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 会签或者或签完成条件检查 检查节点是否满足会签或者或签的完成条件
|
||||
* @param execution 执行实例对象
|
||||
* @return boolean 如果节点满足条件则返回true,否则返回false
|
||||
*/
|
||||
public boolean completionCondition(DelegateExecution execution) {
|
||||
ExecutionEntityImpl entity = (ExecutionEntityImpl) execution;
|
||||
String processDefinitionKey = entity.getProcessDefinitionKey();
|
||||
String nodeId = execution.getCurrentActivityId();
|
||||
|
||||
Node node = remoteFlowTaskService.queryNodeOriData(processDefinitionKey, nodeId).getData();
|
||||
Integer multipleMode = node.getMultipleMode();
|
||||
BigDecimal modePercentage = BigDecimal.valueOf(100);
|
||||
|
||||
Object variable = execution.getVariable(StrUtil.format("{}_approve_condition", nodeId));
|
||||
log.debug("当前节点审批结果:{}", variable);
|
||||
Boolean approve = Convert.toBool(variable);
|
||||
|
||||
if (multipleMode == ProcessInstanceConstant.MULTIPLE_MODE_AL_SAME
|
||||
|| multipleMode == ProcessInstanceConstant.MULTIPLE_MODE_ALL_SORT) {
|
||||
// 如果是会签或者顺序签署
|
||||
if (!approve) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (multipleMode == ProcessInstanceConstant.MULTIPLE_MODE_ONE) {
|
||||
// 如果是或签
|
||||
if (approve) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int nrOfInstances = (int) execution.getVariable("nrOfInstances");
|
||||
int nrOfCompletedInstances = (int) execution.getVariable("nrOfCompletedInstances");
|
||||
log.debug("当前节点完成实例数:{} 总实例数:{} 需要完成比例:{}", nrOfCompletedInstances, nrOfInstances, modePercentage);
|
||||
|
||||
if (multipleMode == ProcessInstanceConstant.MULTIPLE_MODE_AL_SAME) {
|
||||
return BigDecimal.valueOf(nrOfCompletedInstances * 100L)
|
||||
.compareTo(BigDecimal.valueOf(nrOfCompletedInstances).multiply(modePercentage)) > 0;
|
||||
}
|
||||
|
||||
return nrOfCompletedInstances == nrOfInstances;
|
||||
}
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package com.pig4cloud.pigx.flow.engine.node.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysUser;
|
||||
import com.pig4cloud.pigx.admin.api.feign.RemoteUserService;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.flow.engine.node.AssignUserStrategy;
|
||||
import com.pig4cloud.pigx.flow.task.constant.NodeUserTypeEnum;
|
||||
import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Node;
|
||||
import com.pig4cloud.pigx.flow.task.dto.NodeUser;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 指定具体用户
|
||||
*
|
||||
* @author Huijun Zhao
|
||||
* @description
|
||||
* @date 2023-07-07 13:42
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Component(ProcessInstanceConstant.AssignedTypeClass.USER + "AssignUserStrategy")
|
||||
public class AssignUserFixedStrategyImpl implements AssignUserStrategy {
|
||||
|
||||
private final RemoteUserService remoteUserService;
|
||||
|
||||
/**
|
||||
* 处理节点并返回用户ID列表。
|
||||
* @param node 节点
|
||||
* @param rootUser 根用户
|
||||
* @param variables 变量
|
||||
* @return 用户ID列表
|
||||
*/
|
||||
@Override
|
||||
public List<Long> handle(Node node, NodeUser rootUser, Map<String, Object> variables) {
|
||||
|
||||
// 指定人员
|
||||
List<NodeUser> userDtoList = node.getNodeUserList();
|
||||
// 用户ID列表
|
||||
List<Long> userIdList = userDtoList.stream()
|
||||
.filter(w -> StrUtil.equals(w.getType(), NodeUserTypeEnum.USER.getKey()))
|
||||
.map(NodeUser::getId)
|
||||
.collect(Collectors.toList());
|
||||
// 部门ID列表
|
||||
List<Long> deptIdList = userDtoList.stream()
|
||||
.filter(w -> StrUtil.equals(w.getType(), NodeUserTypeEnum.DEPT.getKey()))
|
||||
.map(NodeUser::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (CollUtil.isNotEmpty(deptIdList)) {
|
||||
R<List<SysUser>> r = remoteUserService.getUserIdListByDeptIdList(deptIdList);
|
||||
if (CollUtil.isNotEmpty(r.getData())) {
|
||||
for (SysUser user : r.getData()) {
|
||||
if (!userIdList.contains(user.getUserId())) {
|
||||
userIdList.add(user.getUserId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return userIdList;
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.pig4cloud.pigx.flow.engine.node.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.pig4cloud.pigx.flow.engine.node.AssignUserStrategy;
|
||||
import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Node;
|
||||
import com.pig4cloud.pigx.flow.task.dto.NodeUser;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 来自表单
|
||||
*
|
||||
* @author Huijun Zhao
|
||||
* @description
|
||||
* @date 2023-07-07 13:42
|
||||
*/
|
||||
@Component(ProcessInstanceConstant.AssignedTypeClass.FORM_USER + "AssignUserStrategy")
|
||||
public class AssignUserFormStrategyImpl implements AssignUserStrategy {
|
||||
|
||||
@Override
|
||||
public List<Long> handle(Node node, NodeUser rootUser, Map<String, Object> variables) {
|
||||
List<Long> assignList = new ArrayList<>();
|
||||
|
||||
Object variable = variables.get(node.getFormUserId());
|
||||
if (variable != null && !StrUtil.isBlankIfStr(variable)) {
|
||||
String jsonString = JSON.toJSONString(variable);
|
||||
List<NodeUser> nodeUserDtoList = JSONUtil.toList(jsonString, NodeUser.class);
|
||||
assignList.addAll(nodeUserDtoList.stream().map(NodeUser::getId).toList());
|
||||
}
|
||||
|
||||
return assignList;
|
||||
}
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.pig4cloud.pigx.flow.engine.node.impl;
|
||||
|
||||
import com.pig4cloud.pigx.admin.api.feign.RemoteUserService;
|
||||
import com.pig4cloud.pigx.common.core.util.RetOps;
|
||||
import com.pig4cloud.pigx.flow.engine.node.AssignUserStrategy;
|
||||
import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Node;
|
||||
import com.pig4cloud.pigx.flow.task.dto.NodeUser;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 来自角色
|
||||
*
|
||||
* @author Huijun Zhao
|
||||
* @description
|
||||
* @date 2023-07-07 13:42
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Component(ProcessInstanceConstant.AssignedTypeClass.ROLE + "AssignUserStrategy")
|
||||
public class AssignUserRoleStrategyImpl implements AssignUserStrategy {
|
||||
|
||||
private final RemoteUserService remoteUserService;
|
||||
|
||||
/**
|
||||
* 处理节点并返回用户ID列表。
|
||||
* @param node 节点
|
||||
* @param rootUser 根用户
|
||||
* @param variables 变量
|
||||
* @return 用户ID列表
|
||||
*/
|
||||
@Override
|
||||
public List<Long> handle(Node node, NodeUser rootUser, Map<String, Object> variables) {
|
||||
// 使用 lambda 表达式和方法引用从 NodeUser 列表中提取角色 ID
|
||||
List<Long> roleIds = node.getNodeUserList().stream().map(NodeUser::getId).collect(Collectors.toList());
|
||||
// 提取 Optional 结果中的数据
|
||||
List<Long> data = RetOps.of(remoteUserService.getUserIdListByRoleIdList(roleIds))
|
||||
.getData()
|
||||
.orElseGet(Collections::emptyList);
|
||||
|
||||
// 返回用户 ID 列表
|
||||
return new ArrayList<>(data);
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.pig4cloud.pigx.flow.engine.node.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.pig4cloud.pigx.flow.engine.node.AssignUserStrategy;
|
||||
import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Node;
|
||||
import com.pig4cloud.pigx.flow.task.dto.NodeUser;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 发起人自选
|
||||
*
|
||||
* @author Huijun Zhao
|
||||
* @description
|
||||
* @date 2023-07-07 13:42
|
||||
*/
|
||||
@Slf4j
|
||||
@Component(ProcessInstanceConstant.AssignedTypeClass.SELF_SELECT + "AssignUserStrategy")
|
||||
public class AssignUserSelfSelectStrategyImpl implements AssignUserStrategy {
|
||||
|
||||
@Override
|
||||
public List<Long> handle(Node node, NodeUser rootUser, Map<String, Object> variables) {
|
||||
|
||||
List<Long> assignList = new ArrayList<>();
|
||||
|
||||
Object variable = variables.get(StrUtil.format("{}_assignee_select", node.getId()));
|
||||
log.info("{}-发起人自选参数:{}", node.getName(), variable);
|
||||
if (variable == null) {
|
||||
return assignList;
|
||||
}
|
||||
List<NodeUser> nodeUserDtos = JSON.parseArray(JSON.toJSONString(variable), NodeUser.class);
|
||||
|
||||
List<Long> collect = nodeUserDtos.stream().map(NodeUser::getId).toList();
|
||||
|
||||
assignList.addAll(collect);
|
||||
return assignList;
|
||||
}
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.pig4cloud.pigx.flow.engine.node.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.pig4cloud.pigx.flow.engine.node.AssignUserStrategy;
|
||||
import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Node;
|
||||
import com.pig4cloud.pigx.flow.task.dto.NodeUser;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 发起人自己
|
||||
*
|
||||
* @author Huijun Zhao
|
||||
* @description
|
||||
* @date 2023-07-07 13:42
|
||||
*/
|
||||
@Component(ProcessInstanceConstant.AssignedTypeClass.SELF + "AssignUserStrategy")
|
||||
public class AssignUserSelfStrategyImpl implements AssignUserStrategy {
|
||||
|
||||
@Override
|
||||
public List<Long> handle(Node node, NodeUser rootUser, Map<String, Object> variables) {
|
||||
return CollUtil.newArrayList(rootUser.getId());
|
||||
}
|
||||
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package com.pig4cloud.pigx.flow.engine.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.flow.task.api.feign.RemoteFlowTaskService;
|
||||
import com.pig4cloud.pigx.flow.task.constant.NodeUserTypeEnum;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Node;
|
||||
import com.pig4cloud.pigx.flow.task.dto.NodeUser;
|
||||
import com.pig4cloud.pigx.flow.task.dto.ProcessCopyDto;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.JavaDelegate;
|
||||
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 抄送任务处理器--java服务任务
|
||||
*/
|
||||
public class CopyServiceTask implements JavaDelegate {
|
||||
|
||||
/**
|
||||
* 执行给定执行的任务。
|
||||
* @param execution 要处理的执行
|
||||
*/
|
||||
@Override
|
||||
public void execute(DelegateExecution execution) {
|
||||
|
||||
ExecutionEntityImpl entity = (ExecutionEntityImpl) execution;
|
||||
String nodeId = entity.getActivityId();
|
||||
String flowId = entity.getProcessDefinitionKey();
|
||||
|
||||
RemoteFlowTaskService remoteFlowTaskService = SpringUtil.getBean(RemoteFlowTaskService.class);
|
||||
Node node = remoteFlowTaskService.queryNodeOriData(flowId, nodeId).getData();
|
||||
|
||||
// 获取指定人员
|
||||
List<NodeUser> userDtoList = node.getNodeUserList();
|
||||
// 用户ID列表
|
||||
List<String> userIdList = userDtoList.stream()
|
||||
.filter(w -> StrUtil.equals(w.getType(), NodeUserTypeEnum.USER.getKey()))
|
||||
.map(w -> Convert.toStr(w.getId()))
|
||||
.collect(Collectors.toList());
|
||||
// 部门ID列表
|
||||
List<String> deptIdList = userDtoList.stream()
|
||||
.filter(w -> StrUtil.equals(w.getType(), NodeUserTypeEnum.DEPT.getKey()))
|
||||
.map(w -> Convert.toStr(w.getId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (CollUtil.isNotEmpty(deptIdList)) {
|
||||
|
||||
R<List<String>> r = remoteFlowTaskService.queryUserIdListByDepIdList(deptIdList);
|
||||
|
||||
List<String> data = r.getData();
|
||||
if (CollUtil.isNotEmpty(data)) {
|
||||
for (String datum : data) {
|
||||
if (!userIdList.contains(datum)) {
|
||||
userIdList.add(datum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取发起人
|
||||
Object rootUserObj = execution.getVariable("root");
|
||||
NodeUser rootUser = JSON.parseArray(JSON.toJSONString(rootUserObj), NodeUser.class).get(0);
|
||||
|
||||
Map<String, Object> variables = execution.getVariables();
|
||||
|
||||
for (String userIds : userIdList) {
|
||||
// 发送抄送任务
|
||||
ProcessCopyDto processCopyDto = new ProcessCopyDto();
|
||||
processCopyDto.setNodeTime(new Date());
|
||||
processCopyDto.setStartUserId(rootUser.getId());
|
||||
processCopyDto.setFlowId(flowId);
|
||||
processCopyDto.setProcessInstanceId(execution.getProcessInstanceId());
|
||||
processCopyDto.setNodeId(nodeId);
|
||||
processCopyDto.setNodeName(entity.getActivityName());
|
||||
processCopyDto.setFormData(JSON.toJSONString(variables));
|
||||
processCopyDto.setUserId(Long.parseLong(userIds));
|
||||
remoteFlowTaskService.saveCC(processCopyDto);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/*
|
||||
* @author pigx archetype
|
||||
*/
|
||||
package com.pig4cloud.pigx.flow.engine.service;
|
||||
+664
@@ -0,0 +1,664 @@
|
||||
package com.pig4cloud.pigx.flow.engine.utils;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.flowable.bpmn.model.*;
|
||||
import org.flowable.engine.history.HistoricActivityInstance;
|
||||
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
|
||||
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 流程引擎工具类封装
|
||||
*
|
||||
* @author: linjinp
|
||||
* @create: 2019-12-24 13:51
|
||||
**/
|
||||
public class FlowableUtils {
|
||||
|
||||
public static final Logger logger = LogManager.getLogger(FlowableUtils.class);
|
||||
|
||||
/**
|
||||
* 根据节点获取入口连线集合
|
||||
* @param source 流程元素节点
|
||||
* @return 入口连线集合
|
||||
*/
|
||||
public static List<SequenceFlow> getElementIncomingFlows(FlowElement source) {
|
||||
List<SequenceFlow> sequenceFlows = null;
|
||||
if (source instanceof Task) {
|
||||
sequenceFlows = ((Task) source).getIncomingFlows();
|
||||
}
|
||||
else if (source instanceof Gateway) {
|
||||
sequenceFlows = ((Gateway) source).getIncomingFlows();
|
||||
}
|
||||
else if (source instanceof SubProcess) {
|
||||
sequenceFlows = ((SubProcess) source).getIncomingFlows();
|
||||
}
|
||||
else if (source instanceof StartEvent) {
|
||||
sequenceFlows = ((StartEvent) source).getIncomingFlows();
|
||||
}
|
||||
else if (source instanceof EndEvent) {
|
||||
sequenceFlows = ((EndEvent) source).getIncomingFlows();
|
||||
}
|
||||
return sequenceFlows;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据节点获取出口连线集合
|
||||
* @param source 流程元素节点
|
||||
* @return 出口连线集合
|
||||
*/
|
||||
public static List<SequenceFlow> getElementOutgoingFlows(FlowElement source) {
|
||||
List<SequenceFlow> sequenceFlows = null;
|
||||
if (source instanceof Task) {
|
||||
sequenceFlows = ((Task) source).getOutgoingFlows();
|
||||
}
|
||||
else if (source instanceof Gateway) {
|
||||
sequenceFlows = ((Gateway) source).getOutgoingFlows();
|
||||
}
|
||||
else if (source instanceof SubProcess) {
|
||||
sequenceFlows = ((SubProcess) source).getOutgoingFlows();
|
||||
}
|
||||
else if (source instanceof StartEvent) {
|
||||
sequenceFlows = ((StartEvent) source).getOutgoingFlows();
|
||||
}
|
||||
else if (source instanceof EndEvent) {
|
||||
sequenceFlows = ((EndEvent) source).getOutgoingFlows();
|
||||
}
|
||||
return sequenceFlows;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归获取流程定义所有的流程元素
|
||||
* @param flowElements 当前级别流程元素集合
|
||||
* @param allElements 用于存储的全部流程元素集合
|
||||
* @return 全部流程元素集合
|
||||
*/
|
||||
public static Collection<FlowElement> getAllElements(Collection<FlowElement> flowElements,
|
||||
Collection<FlowElement> allElements) {
|
||||
allElements = allElements == null ? new ArrayList<>() : allElements;
|
||||
|
||||
for (FlowElement flowElement : flowElements) {
|
||||
allElements.add(flowElement);
|
||||
if (flowElement instanceof SubProcess) {
|
||||
// 继续深入子流程,进一步获取子流程
|
||||
allElements = FlowableUtils.getAllElements(((SubProcess) flowElement).getFlowElements(), allElements);
|
||||
}
|
||||
}
|
||||
return allElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从后向前递归获取父级用户任务节点
|
||||
* @param source 当前节点
|
||||
* @param hasSequenceFlow 已处理过的顺序流集合,避免循环
|
||||
* @param userTaskList 递归获取的用户任务节点集合
|
||||
* @return 用户任务节点集合
|
||||
*/
|
||||
public static List<UserTask> iteratorFindParentUserTasks(FlowElement source, Set<String> hasSequenceFlow,
|
||||
List<UserTask> userTaskList) {
|
||||
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
|
||||
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
|
||||
if (source instanceof StartEvent && source.getSubProcess() != null) {
|
||||
userTaskList = iteratorFindParentUserTasks(source.getSubProcess(), hasSequenceFlow, userTaskList);
|
||||
}
|
||||
|
||||
// 根据类型,获取入口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
|
||||
|
||||
if (sequenceFlows != null) {
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 类型为用户节点,则新增父级节点
|
||||
if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
|
||||
userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement());
|
||||
continue;
|
||||
}
|
||||
// 类型为子流程,则添加子流程开始节点出口处相连的节点
|
||||
if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
|
||||
// 获取子流程用户任务节点
|
||||
List<UserTask> childUserTaskList = findChildProcessUserTasks(
|
||||
(StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements()
|
||||
.toArray()[0],
|
||||
null, null);
|
||||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
||||
if (childUserTaskList != null && childUserTaskList.size() > 0) {
|
||||
userTaskList.addAll(childUserTaskList);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 继续迭代
|
||||
// 注意:已经经过的节点与连线都应该用浅拷贝出来的对象
|
||||
// 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据
|
||||
userTaskList = iteratorFindParentUserTasks(sequenceFlow.getSourceFlowElement(),
|
||||
new HashSet<>(hasSequenceFlow), userTaskList);
|
||||
}
|
||||
}
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找
|
||||
* @param source 起始节点
|
||||
* @param runActiveIdList 正在运行的任务 Key,用于校验任务节点是否是正在运行的节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param flowElementList 需要撤回的用户任务列表
|
||||
* @return
|
||||
*/
|
||||
public static List<FlowElement> iteratorFindChildUserTasks(FlowElement source, List<String> runActiveIdList,
|
||||
Set<String> hasSequenceFlow, List<FlowElement> flowElementList) {
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
flowElementList = flowElementList == null ? new ArrayList<>() : flowElementList;
|
||||
|
||||
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
|
||||
if (source instanceof EndEvent && source.getSubProcess() != null) {
|
||||
flowElementList = iteratorFindChildUserTasks(source.getSubProcess(), runActiveIdList, hasSequenceFlow,
|
||||
flowElementList);
|
||||
}
|
||||
|
||||
// 根据类型,获取出口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
|
||||
|
||||
if (sequenceFlows != null) {
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 如果为用户任务类型,或者为网关
|
||||
// 活动节点ID 在运行的任务中存在,添加
|
||||
if ((sequenceFlow.getTargetFlowElement() instanceof UserTask
|
||||
|| sequenceFlow.getTargetFlowElement() instanceof Gateway)
|
||||
&& runActiveIdList.contains((sequenceFlow.getTargetFlowElement()).getId())) {
|
||||
flowElementList.add(sequenceFlow.getTargetFlowElement());
|
||||
continue;
|
||||
}
|
||||
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
|
||||
List<FlowElement> childUserTaskList = iteratorFindChildUserTasks(
|
||||
(FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements()
|
||||
.toArray()[0]),
|
||||
runActiveIdList, hasSequenceFlow, null);
|
||||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
||||
if (childUserTaskList != null && childUserTaskList.size() > 0) {
|
||||
flowElementList.addAll(childUserTaskList);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 继续迭代
|
||||
// 注意:已经经过的节点与连线都应该用浅拷贝出来的对象
|
||||
// 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据
|
||||
flowElementList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runActiveIdList,
|
||||
new HashSet<>(hasSequenceFlow), flowElementList);
|
||||
}
|
||||
}
|
||||
return flowElementList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 迭代获取子流程用户任务节点
|
||||
* @param source 起始节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param userTaskList 需要撤回的用户任务列表
|
||||
* @return
|
||||
*/
|
||||
public static List<UserTask> findChildProcessUserTasks(FlowElement source, Set<String> hasSequenceFlow,
|
||||
List<UserTask> userTaskList) {
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
|
||||
|
||||
// 根据类型,获取出口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
|
||||
|
||||
if (sequenceFlows != null) {
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof UserTask) {
|
||||
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
|
||||
continue;
|
||||
}
|
||||
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
|
||||
List<UserTask> childUserTaskList = findChildProcessUserTasks(
|
||||
(FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements()
|
||||
.toArray()[0]),
|
||||
hasSequenceFlow, null);
|
||||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
||||
if (childUserTaskList != null && childUserTaskList.size() > 0) {
|
||||
userTaskList.addAll(childUserTaskList);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 继续迭代
|
||||
// 注意:已经经过的节点与连线都应该用浅拷贝出来的对象
|
||||
// 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据
|
||||
userTaskList = findChildProcessUserTasks(sequenceFlow.getTargetFlowElement(),
|
||||
new HashSet<>(hasSequenceFlow), userTaskList);
|
||||
}
|
||||
}
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从后向前寻路,获取所有脏线路上的点
|
||||
* @param source 起始节点
|
||||
* @param passRoads 已经经过的点集合
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param targets 目标脏线路终点
|
||||
* @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储
|
||||
* @return
|
||||
*/
|
||||
public static Set<String> iteratorFindDirtyRoads(FlowElement source, List<String> passRoads,
|
||||
Set<String> hasSequenceFlow, List<String> targets, Set<String> dirtyRoads) {
|
||||
passRoads = passRoads == null ? new ArrayList<>() : passRoads;
|
||||
dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads;
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
|
||||
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
|
||||
if (source instanceof StartEvent && source.getSubProcess() != null) {
|
||||
dirtyRoads = iteratorFindDirtyRoads(source.getSubProcess(), passRoads, hasSequenceFlow, targets,
|
||||
dirtyRoads);
|
||||
}
|
||||
|
||||
// 根据类型,获取入口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
|
||||
|
||||
if (sequenceFlows != null) {
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 新增经过的路线
|
||||
passRoads.add(sequenceFlow.getSourceFlowElement().getId());
|
||||
// 如果此点为目标点,确定经过的路线为脏线路,添加点到脏线路中,然后找下个连线
|
||||
if (targets.contains(sequenceFlow.getSourceFlowElement().getId())) {
|
||||
dirtyRoads.addAll(passRoads);
|
||||
continue;
|
||||
}
|
||||
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
|
||||
if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
|
||||
dirtyRoads = findChildProcessAllDirtyRoad(
|
||||
(StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements()
|
||||
.toArray()[0],
|
||||
null, dirtyRoads);
|
||||
// 是否存在子流程上,true 是,false 否
|
||||
Boolean isInChildProcess = dirtyTargetInChildProcess(
|
||||
(StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements()
|
||||
.toArray()[0],
|
||||
null, targets, null);
|
||||
if (isInChildProcess) {
|
||||
// 已在子流程上找到,该路线结束
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 继续迭代
|
||||
// 注意:已经经过的节点与连线都应该用浅拷贝出来的对象
|
||||
// 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据
|
||||
dirtyRoads = iteratorFindDirtyRoads(sequenceFlow.getSourceFlowElement(), new ArrayList<>(passRoads),
|
||||
new HashSet<>(hasSequenceFlow), targets, dirtyRoads);
|
||||
}
|
||||
}
|
||||
return dirtyRoads;
|
||||
}
|
||||
|
||||
/**
|
||||
* 迭代获取子流程脏路线 说明,假如回退的点就是子流程,那么也肯定会回退到子流程最初的用户任务节点,因此子流程中的节点全是脏路线
|
||||
* @param source 起始节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储
|
||||
* @return
|
||||
*/
|
||||
public static Set<String> findChildProcessAllDirtyRoad(FlowElement source, Set<String> hasSequenceFlow,
|
||||
Set<String> dirtyRoads) {
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads;
|
||||
|
||||
// 根据类型,获取出口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
|
||||
|
||||
if (sequenceFlows != null) {
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 添加脏路线
|
||||
dirtyRoads.add(sequenceFlow.getTargetFlowElement().getId());
|
||||
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
|
||||
dirtyRoads = findChildProcessAllDirtyRoad(
|
||||
(FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements()
|
||||
.toArray()[0]),
|
||||
hasSequenceFlow, dirtyRoads);
|
||||
}
|
||||
// 继续迭代
|
||||
// 注意:已经经过的节点与连线都应该用浅拷贝出来的对象
|
||||
// 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据
|
||||
dirtyRoads = findChildProcessAllDirtyRoad(sequenceFlow.getTargetFlowElement(),
|
||||
new HashSet<>(hasSequenceFlow), dirtyRoads);
|
||||
}
|
||||
}
|
||||
return dirtyRoads;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断脏路线结束节点是否在子流程上
|
||||
* @param source 起始节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param targets 判断脏路线节点是否存在子流程上,只要存在一个,说明脏路线只到子流程为止
|
||||
* @param inChildProcess 是否存在子流程上,true 是,false 否
|
||||
* @return
|
||||
*/
|
||||
public static Boolean dirtyTargetInChildProcess(FlowElement source, Set<String> hasSequenceFlow,
|
||||
List<String> targets, Boolean inChildProcess) {
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
inChildProcess = inChildProcess == null ? false : inChildProcess;
|
||||
|
||||
// 根据类型,获取出口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
|
||||
|
||||
if (sequenceFlows != null && !inChildProcess) {
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 如果发现目标点在子流程上存在,说明只到子流程为止
|
||||
if (targets.contains(sequenceFlow.getTargetFlowElement().getId())) {
|
||||
inChildProcess = true;
|
||||
break;
|
||||
}
|
||||
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
|
||||
inChildProcess = dirtyTargetInChildProcess(
|
||||
(FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements()
|
||||
.toArray()[0]),
|
||||
hasSequenceFlow, targets, inChildProcess);
|
||||
}
|
||||
// 继续迭代
|
||||
// 注意:已经经过的节点与连线都应该用浅拷贝出来的对象
|
||||
// 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据
|
||||
inChildProcess = dirtyTargetInChildProcess(sequenceFlow.getTargetFlowElement(),
|
||||
new HashSet<>(hasSequenceFlow), targets, inChildProcess);
|
||||
}
|
||||
}
|
||||
return inChildProcess;
|
||||
}
|
||||
|
||||
/**
|
||||
* 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况
|
||||
* @param source 起始节点
|
||||
* @param isSequential 是否串行
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param targetKsy 目标节点
|
||||
* @return
|
||||
*/
|
||||
public static Boolean iteratorCheckSequentialReferTarget(FlowElement source, String targetKsy,
|
||||
Set<String> hasSequenceFlow, Boolean isSequential) {
|
||||
isSequential = isSequential == null ? true : isSequential;
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
|
||||
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
|
||||
if (source instanceof StartEvent && source.getSubProcess() != null) {
|
||||
isSequential = iteratorCheckSequentialReferTarget(source.getSubProcess(), targetKsy, hasSequenceFlow,
|
||||
isSequential);
|
||||
}
|
||||
|
||||
// 根据类型,获取入口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
|
||||
|
||||
if (sequenceFlows != null) {
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 如果目标节点已被判断为并行,后面都不需要执行,直接返回
|
||||
if (isSequential == false) {
|
||||
break;
|
||||
}
|
||||
// 这条线路存在目标节点,这条线路完成,进入下个线路
|
||||
if (targetKsy.equals(sequenceFlow.getSourceFlowElement().getId())) {
|
||||
continue;
|
||||
}
|
||||
if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) {
|
||||
isSequential = false;
|
||||
break;
|
||||
}
|
||||
// 否则就继续迭代
|
||||
// 注意:已经经过的节点与连线都应该用浅拷贝出来的对象
|
||||
// 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据
|
||||
isSequential = iteratorCheckSequentialReferTarget(sequenceFlow.getSourceFlowElement(), targetKsy,
|
||||
new HashSet<>(hasSequenceFlow), isSequential);
|
||||
}
|
||||
}
|
||||
return isSequential;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从后向前寻路,获取到达节点的所有路线 不存在直接回退到子流程,但是存在回退到父级流程的情况
|
||||
* @param source 起始节点
|
||||
* @param passRoads 已经经过的点集合
|
||||
* @param roads 路线
|
||||
* @return
|
||||
*/
|
||||
public static List<List<UserTask>> findRoad(FlowElement source, List<UserTask> passRoads,
|
||||
Set<String> hasSequenceFlow, List<List<UserTask>> roads) {
|
||||
passRoads = passRoads == null ? new ArrayList<>() : passRoads;
|
||||
roads = roads == null ? new ArrayList<>() : roads;
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
|
||||
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
|
||||
if (source instanceof StartEvent && source.getSubProcess() != null) {
|
||||
roads = findRoad(source.getSubProcess(), passRoads, hasSequenceFlow, roads);
|
||||
}
|
||||
|
||||
// 根据类型,获取入口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
|
||||
|
||||
if (sequenceFlows != null && sequenceFlows.size() != 0) {
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 添加经过路线
|
||||
if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
|
||||
passRoads.add((UserTask) sequenceFlow.getSourceFlowElement());
|
||||
}
|
||||
// 继续迭代
|
||||
// 注意:已经经过的节点与连线都应该用浅拷贝出来的对象
|
||||
// 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据
|
||||
roads = findRoad(sequenceFlow.getSourceFlowElement(), new ArrayList<>(passRoads),
|
||||
new HashSet<>(hasSequenceFlow), roads);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 添加路线
|
||||
roads.add(passRoads);
|
||||
}
|
||||
return roads;
|
||||
}
|
||||
|
||||
/**
|
||||
* 历史节点数据清洗,清洗掉又回滚导致的脏数据
|
||||
* @param allElements 全部节点信息
|
||||
* @param historicActivityIdList 历史任务实例信息,数据采用开始时间升序
|
||||
* @return
|
||||
*/
|
||||
public static List<String> historicTaskInstanceClean(Collection<FlowElement> allElements,
|
||||
List<HistoricActivityInstance> historicActivityIdList) {
|
||||
// 会签节点收集
|
||||
List<String> multiTask = new ArrayList<>();
|
||||
allElements.forEach(flowElement -> {
|
||||
if (flowElement instanceof UserTask) {
|
||||
// 如果该节点的行为为会签行为,说明该节点为会签节点
|
||||
if (((UserTask) flowElement).getBehavior() instanceof ParallelMultiInstanceBehavior
|
||||
|| ((UserTask) flowElement).getBehavior() instanceof SequentialMultiInstanceBehavior) {
|
||||
multiTask.add(flowElement.getId());
|
||||
}
|
||||
}
|
||||
});
|
||||
// 循环放入栈,栈 LIFO:后进先出
|
||||
Stack<HistoricActivityInstance> stack = new Stack<>();
|
||||
historicActivityIdList.forEach(item -> stack.push(item));
|
||||
// 清洗后的历史任务实例
|
||||
List<String> lastHistoricTaskInstanceList = new ArrayList<>();
|
||||
// 网关存在可能只走了部分分支情况,且还存在跳转废弃数据以及其他分支数据的干扰,因此需要对历史节点数据进行清洗
|
||||
// 临时用户任务 key
|
||||
StringBuilder userTaskKey = null;
|
||||
// 临时被删掉的任务 key,存在并行情况
|
||||
List<String> deleteKeyList = new ArrayList<>();
|
||||
// 临时脏数据线路
|
||||
List<Set<String>> dirtyDataLineList = new ArrayList<>();
|
||||
// 由某个点跳到会签点,此时出现多个会签实例对应 1 个跳转情况,需要把这些连续脏数据都找到
|
||||
// 会签特殊处理下标
|
||||
int multiIndex = -1;
|
||||
// 会签特殊处理 key
|
||||
StringBuilder multiKey = null;
|
||||
// 会签特殊处理操作标识
|
||||
boolean multiOpera = false;
|
||||
while (!stack.empty()) {
|
||||
// 从这里开始 userTaskKey 都还是上个栈的 key
|
||||
// 是否是脏数据线路上的点
|
||||
final boolean[] isDirtyData = { false };
|
||||
for (Set<String> oldDirtyDataLine : dirtyDataLineList) {
|
||||
if (oldDirtyDataLine.contains(stack.peek().getActivityId())) {
|
||||
isDirtyData[0] = true;
|
||||
}
|
||||
}
|
||||
// 删除原因不为空,说明从这条数据开始回跳或者回退的
|
||||
// MI_END:会签完成后,其他未签到节点的删除原因,不在处理范围内
|
||||
if (stack.peek().getDeleteReason() != null && !stack.peek().getDeleteReason().equals("MI_END")) {
|
||||
// 可以理解为脏线路起点
|
||||
String dirtyPoint = "";
|
||||
if (stack.peek().getDeleteReason().indexOf("Change activity to ") >= 0) {
|
||||
dirtyPoint = stack.peek().getDeleteReason().replace("Change activity to ", "");
|
||||
}
|
||||
// 会签回退删除原因有点不同
|
||||
if (stack.peek().getDeleteReason().indexOf("Change parent activity to ") >= 0) {
|
||||
dirtyPoint = stack.peek().getDeleteReason().replace("Change parent activity to ", "");
|
||||
}
|
||||
FlowElement dirtyTask = null;
|
||||
// 获取变更节点的对应的入口处连线
|
||||
// 如果是网关并行回退情况,会变成两条脏数据路线,效果一样
|
||||
for (FlowElement flowElement : allElements) {
|
||||
if (flowElement.getId().equals(stack.peek().getActivityId())) {
|
||||
dirtyTask = flowElement;
|
||||
}
|
||||
}
|
||||
// 获取脏数据线路
|
||||
Set<String> dirtyDataLine = FlowableUtils.iteratorFindDirtyRoads(dirtyTask, null, null,
|
||||
Arrays.asList(dirtyPoint.split(",")), null);
|
||||
// 自己本身也是脏线路上的点,加进去
|
||||
dirtyDataLine.add(stack.peek().getActivityId());
|
||||
logger.info(stack.peek().getActivityId() + "点脏路线集合:" + dirtyDataLine);
|
||||
// 是全新的需要添加的脏线路
|
||||
boolean isNewDirtyData = true;
|
||||
for (int i = 0; i < dirtyDataLineList.size(); i++) {
|
||||
// 如果发现他的上个节点在脏线路内,说明这个点可能是并行的节点,或者连续驳回
|
||||
// 这时,都以之前的脏线路节点为标准,只需合并脏线路即可,也就是路线补全
|
||||
if (dirtyDataLineList.get(i).contains(userTaskKey.toString())) {
|
||||
isNewDirtyData = false;
|
||||
dirtyDataLineList.get(i).addAll(dirtyDataLine);
|
||||
}
|
||||
}
|
||||
// 已确定时全新的脏线路
|
||||
if (isNewDirtyData) {
|
||||
// deleteKey 单一路线驳回到并行,这种同时生成多个新实例记录情况,这时 deleteKey 其实是由多个值组成
|
||||
// 按照逻辑,回退后立刻生成的实例记录就是回退的记录
|
||||
// 至于驳回所生成的 Key,直接从删除原因中获取,因为存在驳回到并行的情况
|
||||
deleteKeyList.add(dirtyPoint + ",");
|
||||
dirtyDataLineList.add(dirtyDataLine);
|
||||
}
|
||||
// 添加后,现在这个点变成脏线路上的点了
|
||||
isDirtyData[0] = true;
|
||||
}
|
||||
// 如果不是脏线路上的点,说明是有效数据,添加历史实例 Key
|
||||
if (!isDirtyData[0]) {
|
||||
lastHistoricTaskInstanceList.add(stack.peek().getActivityId());
|
||||
}
|
||||
// 校验脏线路是否结束
|
||||
for (int i = 0; i < deleteKeyList.size(); i++) {
|
||||
// 如果发现脏数据属于会签,记录下下标与对应 Key,以备后续比对,会签脏数据范畴开始
|
||||
if (multiKey == null && multiTask.contains(stack.peek().getActivityId())
|
||||
&& deleteKeyList.get(i).contains(stack.peek().getActivityId())) {
|
||||
multiIndex = i;
|
||||
multiKey = new StringBuilder(stack.peek().getActivityId());
|
||||
}
|
||||
// 会签脏数据处理,节点退回会签清空
|
||||
// 如果在会签脏数据范畴中发现 Key改变,说明会签脏数据在上个节点就结束了,可以把会签脏数据删掉
|
||||
if (multiKey != null && !multiKey.toString().equals(stack.peek().getActivityId())) {
|
||||
deleteKeyList.set(multiIndex,
|
||||
deleteKeyList.get(multiIndex).replace(stack.peek().getActivityId() + ",", ""));
|
||||
multiKey = null;
|
||||
// 结束进行下校验删除
|
||||
multiOpera = true;
|
||||
}
|
||||
// 其他脏数据处理
|
||||
// 发现该路线最后一条脏数据,说明这条脏数据线路处理完了,删除脏数据信息
|
||||
// 脏数据产生的新实例中是否包含这条数据
|
||||
if (multiKey == null && deleteKeyList.get(i).contains(stack.peek().getActivityId())) {
|
||||
// 删除匹配到的部分
|
||||
deleteKeyList.set(i, deleteKeyList.get(i).replace(stack.peek().getActivityId() + ",", ""));
|
||||
}
|
||||
// 如果每组中的元素都以匹配过,说明脏数据结束
|
||||
if ("".equals(deleteKeyList.get(i))) {
|
||||
// 同时删除脏数据
|
||||
deleteKeyList.remove(i);
|
||||
dirtyDataLineList.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 会签数据处理需要在循环外处理,否则可能导致溢出
|
||||
// 会签的数据肯定是之前放进去的所以理论上不会溢出,但还是校验下
|
||||
if (multiOpera && deleteKeyList.size() > multiIndex && "".equals(deleteKeyList.get(multiIndex))) {
|
||||
// 同时删除脏数据
|
||||
deleteKeyList.remove(multiIndex);
|
||||
dirtyDataLineList.remove(multiIndex);
|
||||
multiIndex = -1;
|
||||
multiOpera = false;
|
||||
}
|
||||
// pop() 方法与 peek() 方法不同,在返回值的同时,会把值从栈中移除
|
||||
// 保存新的 userTaskKey 在下个循环中使用
|
||||
userTaskKey = new StringBuilder(stack.pop().getActivityId());
|
||||
}
|
||||
logger.info("清洗后的历史节点数据:" + lastHistoricTaskInstanceList);
|
||||
return lastHistoricTaskInstanceList;
|
||||
}
|
||||
|
||||
}
|
||||
+595
@@ -0,0 +1,595 @@
|
||||
package com.pig4cloud.pigx.flow.engine.utils;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.pig4cloud.pigx.flow.engine.expression.condition.NodeExpressionStrategyFactory;
|
||||
import com.pig4cloud.pigx.flow.engine.listeners.ApprovalCreateListener;
|
||||
import com.pig4cloud.pigx.flow.engine.listeners.FlowProcessEventListener;
|
||||
import com.pig4cloud.pigx.flow.engine.service.CopyServiceTask;
|
||||
import com.pig4cloud.pigx.flow.task.api.feign.RemoteFlowTaskService;
|
||||
import com.pig4cloud.pigx.flow.task.constant.NodeTypeEnum;
|
||||
import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant;
|
||||
import com.pig4cloud.pigx.flow.task.dto.Node;
|
||||
import com.pig4cloud.pigx.flow.task.dto.ProcessNodeDataDto;
|
||||
import com.pig4cloud.pigx.flow.task.utils.NodeUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.Process;
|
||||
import org.flowable.bpmn.model.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 模型工具类 处理模型构建相关的
|
||||
*/
|
||||
@Slf4j
|
||||
public class ModelUtil {
|
||||
|
||||
/**
|
||||
* 构建BPMN流程模型
|
||||
* @param nodeDto 前端传过来的节点数据
|
||||
* @param processName 流程名称
|
||||
* @param flowId 流程定义ID
|
||||
* @return 流程模型对象
|
||||
*/
|
||||
public static BpmnModel buildBpmnModel(Node nodeDto, String processName, String flowId) {
|
||||
BpmnModel bpmnModel = new BpmnModel();
|
||||
bpmnModel.setTargetNamespace("MODEL");
|
||||
|
||||
Process process = new Process();
|
||||
process.setId(flowId);
|
||||
process.setName(processName);
|
||||
|
||||
// 流程监听器
|
||||
ArrayList<EventListener> eventListeners = new ArrayList<>();
|
||||
|
||||
{
|
||||
// 流程实例监听器
|
||||
EventListener eventListener = new EventListener();
|
||||
|
||||
eventListener.setImplementationType("class");
|
||||
eventListener.setImplementation(FlowProcessEventListener.class.getCanonicalName());
|
||||
|
||||
eventListeners.add(eventListener);
|
||||
|
||||
}
|
||||
process.setEventListeners(eventListeners);
|
||||
|
||||
// 构建结束节点
|
||||
|
||||
Node endNodeDto = new Node();
|
||||
endNodeDto.setId(StrUtil.format("end"));
|
||||
endNodeDto.setHeadId(endNodeDto.getId());
|
||||
endNodeDto.setName("endNode");
|
||||
|
||||
// 构建节点
|
||||
EndEvent endEvent = buildEndNode(endNodeDto, false);
|
||||
process.addFlowElement(endEvent);
|
||||
// 创建所有的节点
|
||||
buildAllNode(process, nodeDto, flowId);
|
||||
// 创建所有的内部节点连接线
|
||||
buildAllNodeInnerSequence(process, nodeDto, flowId);
|
||||
// 创建节点间连线
|
||||
buildAllNodeOuterSequence(process, nodeDto, endNodeDto);
|
||||
|
||||
bpmnModel.addProcess(process);
|
||||
return bpmnModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归构建所有节点
|
||||
* @param process 流程对象
|
||||
* @param nodeDto 当前节点数据
|
||||
* @param flowId 流程定义ID
|
||||
*/
|
||||
public static void buildAllNode(Process process, Node nodeDto, String flowId) {
|
||||
if (!NodeUtil.isNode(nodeDto)) {
|
||||
return;
|
||||
}
|
||||
List<FlowElement> flowElementList = buildNode(nodeDto, flowId);
|
||||
for (FlowElement flowElement : flowElementList) {
|
||||
if (process.getFlowElement(flowElement.getId()) == null) {
|
||||
process.addFlowElement(flowElement);
|
||||
}
|
||||
}
|
||||
|
||||
// 子节点
|
||||
Node children = nodeDto.getChildren();
|
||||
if (Objects.equals(nodeDto.getType(), NodeTypeEnum.EXCLUSIVE_GATEWAY.getValue())
|
||||
|| Objects.equals(nodeDto.getType(), NodeTypeEnum.PARALLEL_GATEWAY.getValue())) {
|
||||
|
||||
// 条件分支
|
||||
List<Node> branchs = nodeDto.getConditionNodes();
|
||||
for (Node branch : branchs) {
|
||||
buildAllNode(process, branch.getChildren(), flowId);
|
||||
|
||||
}
|
||||
if (NodeUtil.isNode(children)) {
|
||||
buildAllNode(process, children, flowId);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
if (NodeUtil.isNode(children)) {
|
||||
buildAllNode(process, children, flowId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归构建节点内部的顺序流
|
||||
* @param process 流程对象
|
||||
* @param nodeDto 当前节点数据
|
||||
* @param flowId 流程定义ID
|
||||
*/
|
||||
public static void buildAllNodeInnerSequence(Process process, Node nodeDto, String flowId) {
|
||||
if (!NodeUtil.isNode(nodeDto)) {
|
||||
return;
|
||||
}
|
||||
// 画内部线
|
||||
List<SequenceFlow> flowList = buildInnerSequenceFlow(nodeDto, flowId);
|
||||
for (SequenceFlow sequenceFlow : flowList) {
|
||||
process.addFlowElement(sequenceFlow);
|
||||
}
|
||||
|
||||
// 子节点
|
||||
Node children = nodeDto.getChildren();
|
||||
if (nodeDto.getType() == NodeTypeEnum.EXCLUSIVE_GATEWAY.getValue().intValue()
|
||||
|| nodeDto.getType() == NodeTypeEnum.PARALLEL_GATEWAY.getValue().intValue()) {
|
||||
// 条件分支
|
||||
List<Node> branchs = nodeDto.getConditionNodes();
|
||||
for (Node branch : branchs) {
|
||||
buildAllNodeInnerSequence(process, branch.getChildren(), flowId);
|
||||
|
||||
}
|
||||
if (NodeUtil.isNode(children)) {
|
||||
buildAllNodeInnerSequence(process, children, flowId);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
if (NodeUtil.isNode(children)) {
|
||||
buildAllNodeInnerSequence(process, children, flowId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归构建节点之间的顺序流
|
||||
* @param process 流程对象
|
||||
* @param nodeDto 当前节点数据
|
||||
* @param nextNodeDto 下一个节点数据
|
||||
*/
|
||||
public static void buildAllNodeOuterSequence(Process process, Node nodeDto, Node nextNodeDto) {
|
||||
if (!NodeUtil.isNode(nodeDto)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 子节点
|
||||
Node children = nodeDto.getChildren();
|
||||
if (nodeDto.getType() == NodeTypeEnum.EXCLUSIVE_GATEWAY.getValue().intValue()
|
||||
|| nodeDto.getType() == NodeTypeEnum.PARALLEL_GATEWAY.getValue().intValue()) {
|
||||
// 条件分支
|
||||
List<Node> branchs = nodeDto.getConditionNodes();
|
||||
int ord = 1;
|
||||
int size = branchs.size();
|
||||
for (Node branch : branchs) {
|
||||
buildAllNodeOuterSequence(process, branch.getChildren(),
|
||||
NodeUtil.isNode(children) ? children : nextNodeDto);
|
||||
|
||||
String expression = null;
|
||||
|
||||
if (nodeDto.getType() == NodeTypeEnum.EXCLUSIVE_GATEWAY.getValue().intValue()) {
|
||||
if (ord == size) {
|
||||
expression = NodeExpressionStrategyFactory.handleDefaultBranch(branchs);
|
||||
}
|
||||
else {
|
||||
expression = NodeExpressionStrategyFactory.handle(branch);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 添加连线
|
||||
if (!NodeUtil.isNode(branch.getChildren())) {
|
||||
// 当前分支 没有其他节点了 所有就是网关和网关后面节点直接连线
|
||||
List<SequenceFlow> sequenceFlowList = buildSequenceFlow(
|
||||
NodeUtil.isNode(children) ? children : nextNodeDto, nodeDto, expression);
|
||||
for (SequenceFlow sequenceFlow : sequenceFlowList) {
|
||||
process.addFlowElement(sequenceFlow);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 当前节点 就是网关和第一个节点(非EMPTY节点)连线
|
||||
List<SequenceFlow> sequenceFlowList = buildSequenceFlow(branch.getChildren(), nodeDto, expression);
|
||||
for (SequenceFlow sequenceFlow : sequenceFlowList) {
|
||||
process.addFlowElement(sequenceFlow);
|
||||
}
|
||||
}
|
||||
ord++;
|
||||
|
||||
}
|
||||
if (NodeUtil.isNode(children)) {
|
||||
buildAllNodeOuterSequence(process, children, nextNodeDto);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
// 添加连线
|
||||
List<SequenceFlow> sequenceFlowList = buildSequenceFlow(children, nodeDto, "");
|
||||
for (SequenceFlow sequenceFlow : sequenceFlowList) {
|
||||
process.addFlowElement(sequenceFlow);
|
||||
}
|
||||
if (NodeUtil.isNode(children)) {
|
||||
buildAllNodeOuterSequence(process, children, nextNodeDto);
|
||||
}
|
||||
else {
|
||||
List<SequenceFlow> sequenceFlowList1 = buildSequenceFlow(nextNodeDto, nodeDto, "");
|
||||
for (SequenceFlow sequenceFlow : sequenceFlowList1) {
|
||||
process.addFlowElement(sequenceFlow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建单个节点对象
|
||||
* @param node 节点数据
|
||||
* @param flowId 流程定义ID
|
||||
* @return 流程元素列表
|
||||
*/
|
||||
private static List<FlowElement> buildNode(Node node, String flowId) {
|
||||
List<FlowElement> flowElementList = new ArrayList<>();
|
||||
|
||||
if (!NodeUtil.isNode(node)) {
|
||||
return flowElementList;
|
||||
}
|
||||
|
||||
// 设置节点的连线头节点
|
||||
node.setHeadId(node.getId());
|
||||
// 设置节点的连线尾节点
|
||||
node.setTailId(node.getId());
|
||||
node.setName(StrUtil.format("{}[{}]", node.getName(), RandomUtil.randomNumbers(5)));
|
||||
|
||||
// 存储节点数据
|
||||
RemoteFlowTaskService remoteFlowTaskService = SpringUtil.getBean(RemoteFlowTaskService.class);
|
||||
ProcessNodeDataDto processNodeDataDto = new ProcessNodeDataDto();
|
||||
processNodeDataDto.setFlowId(flowId);
|
||||
processNodeDataDto.setNodeId(node.getId());
|
||||
processNodeDataDto.setData(JSONUtil.toJsonStr(node));
|
||||
remoteFlowTaskService.saveNodeOriData(processNodeDataDto);
|
||||
|
||||
// 开始
|
||||
if (node.getType() == NodeTypeEnum.ROOT.getValue().intValue()) {
|
||||
flowElementList.addAll(buildStartNode(node));
|
||||
}
|
||||
|
||||
// 审批
|
||||
if (node.getType() == NodeTypeEnum.APPROVAL.getValue().intValue()) {
|
||||
|
||||
flowElementList.addAll(buildApproveNode(node));
|
||||
}
|
||||
|
||||
// 抄送
|
||||
if (node.getType() == NodeTypeEnum.CC.getValue().intValue()) {
|
||||
|
||||
flowElementList.add(buildCCNode(node));
|
||||
}
|
||||
// 条件分支
|
||||
if (node.getType() == NodeTypeEnum.EXCLUSIVE_GATEWAY.getValue().intValue()) {
|
||||
|
||||
flowElementList.add(buildExclusiveGatewayNode(node));
|
||||
}
|
||||
// 并行分支
|
||||
if (node.getType() == NodeTypeEnum.PARALLEL_GATEWAY.getValue().intValue()) {
|
||||
|
||||
flowElementList.addAll(buildParallelGatewayNode(node));
|
||||
}
|
||||
return flowElementList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建开始节点 添加一个自动完成任务的用户任务节点
|
||||
* @param node 前端传输节点
|
||||
* @return
|
||||
*/
|
||||
private static List<FlowElement> buildStartNode(Node node) {
|
||||
|
||||
StartEvent startEvent = new StartEvent();
|
||||
startEvent.setId(node.getId());
|
||||
startEvent.setName(node.getName());
|
||||
|
||||
List<FlowElement> flowElementList = new ArrayList<>();
|
||||
flowElementList.add(startEvent);
|
||||
|
||||
return flowElementList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建审批节点
|
||||
* @param node
|
||||
* @return
|
||||
*/
|
||||
private static List<FlowElement> buildApproveNode(Node node) {
|
||||
List<FlowElement> flowElementList = new ArrayList<>();
|
||||
|
||||
node.setTailId(StrUtil.format("approve_gateway_{}", node.getId()));
|
||||
|
||||
// 创建了任务执行监听器
|
||||
// 先执行指派人 后创建
|
||||
// https://tkjohn.github.io/flowable-userguide/#eventDispatcher
|
||||
FlowableListener createListener = new FlowableListener();
|
||||
createListener.setImplementation(ApprovalCreateListener.class.getCanonicalName());
|
||||
createListener.setImplementationType("class");
|
||||
createListener.setEvent("create");
|
||||
|
||||
UserTask userTask = buildUserTask(node, createListener);
|
||||
flowElementList.add(userTask);
|
||||
|
||||
Node exclusiveNode = new Node();
|
||||
exclusiveNode.setId(StrUtil.format("approve_gateway_{}", node.getId()));
|
||||
exclusiveNode.setName("审批-排他网关");
|
||||
flowElementList.add(buildExclusiveGatewayNode(exclusiveNode));
|
||||
// 创建结束节点
|
||||
|
||||
Node endNode = new Node();
|
||||
endNode.setId(StrUtil.format("approve_end_{}", node.getId()));
|
||||
endNode.setName("审批-结束节点");
|
||||
EndEvent endEvent = buildEndNode(endNode, false);
|
||||
flowElementList.add(endEvent);
|
||||
|
||||
{
|
||||
|
||||
// 执行人处理
|
||||
|
||||
String inputDataItem = "${multiInstanceHandler.resolveAssignee(execution)}";
|
||||
|
||||
// 串行
|
||||
|
||||
boolean isSequential = true;
|
||||
|
||||
Integer multipleMode = node.getMultipleMode();
|
||||
// 多人
|
||||
if ((multipleMode == ProcessInstanceConstant.MULTIPLE_MODE_AL_SAME)) {
|
||||
// 并行会签
|
||||
isSequential = false;
|
||||
}
|
||||
// 串行会签
|
||||
if ((multipleMode == ProcessInstanceConstant.MULTIPLE_MODE_ONE)) {
|
||||
|
||||
// 或签
|
||||
isSequential = false;
|
||||
}
|
||||
|
||||
MultiInstanceLoopCharacteristics loopCharacteristics = new MultiInstanceLoopCharacteristics();
|
||||
loopCharacteristics.setSequential(isSequential);
|
||||
loopCharacteristics.setInputDataItem(inputDataItem);
|
||||
loopCharacteristics.setElementVariable(StrUtil.format("{}_assignee_temp", node.getId()));
|
||||
|
||||
loopCharacteristics.setCompletionCondition("${multiInstanceHandler.completionCondition(execution)}");
|
||||
|
||||
userTask.setLoopCharacteristics(loopCharacteristics);
|
||||
String format = StrUtil.format("${{}_assignee_temp}", node.getId());
|
||||
userTask.setAssignee(format);
|
||||
|
||||
}
|
||||
return flowElementList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建用户任务
|
||||
* @param node 前端传输节点
|
||||
* @return
|
||||
*/
|
||||
private static UserTask buildUserTask(Node node, FlowableListener... flowableListeners) {
|
||||
UserTask userTask = new UserTask();
|
||||
userTask.setId(node.getId());
|
||||
userTask.setName(node.getName());
|
||||
|
||||
if (flowableListeners != null) {
|
||||
List<FlowableListener> taskListeners = new ArrayList<>();
|
||||
Collections.addAll(taskListeners, flowableListeners);
|
||||
userTask.setTaskListeners(taskListeners);
|
||||
}
|
||||
|
||||
return userTask;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建排他网关
|
||||
* @param node
|
||||
* @return
|
||||
*/
|
||||
private static FlowElement buildExclusiveGatewayNode(Node node) {
|
||||
ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
|
||||
exclusiveGateway.setId(node.getId());
|
||||
exclusiveGateway.setName(node.getName());
|
||||
return exclusiveGateway;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建并行网关
|
||||
* @param node
|
||||
* @return
|
||||
*/
|
||||
private static List<FlowElement> buildParallelGatewayNode(Node node) {
|
||||
// node.setTailId(StrUtil.format("{}_merge_gateway", node.getId()));
|
||||
|
||||
List<FlowElement> flowElementList = new ArrayList<>();
|
||||
|
||||
ParallelGateway inclusiveGateway = new ParallelGateway();
|
||||
inclusiveGateway.setId(node.getId());
|
||||
inclusiveGateway.setName(node.getName());
|
||||
flowElementList.add(inclusiveGateway);
|
||||
|
||||
// 合并网关
|
||||
ParallelGateway parallelGateway = new ParallelGateway();
|
||||
parallelGateway.setId(StrUtil.format("{}_merge_gateway", node.getId()));
|
||||
parallelGateway.setName(StrUtil.format("{}_合并网关", node.getName()));
|
||||
flowElementList.add(parallelGateway);
|
||||
|
||||
return flowElementList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建结束节点
|
||||
* @param node 前端传输节点
|
||||
* @param terminateAll
|
||||
* @return
|
||||
*/
|
||||
private static EndEvent buildEndNode(Node node, boolean terminateAll) {
|
||||
EndEvent endEvent = new EndEvent();
|
||||
endEvent.setId(node.getId());
|
||||
endEvent.setName(node.getName());
|
||||
|
||||
List<EventDefinition> definitionList = new ArrayList<>();
|
||||
TerminateEventDefinition definition = new TerminateEventDefinition();
|
||||
definition.setTerminateAll(terminateAll);
|
||||
definitionList.add(definition);
|
||||
endEvent.setEventDefinitions(definitionList);
|
||||
|
||||
return endEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建连接线
|
||||
* @param node 子级节点
|
||||
* @param parentNode 父级节点
|
||||
* @param expression
|
||||
* @return 所有连接线
|
||||
*/
|
||||
private static List<SequenceFlow> buildSequenceFlow(Node node, Node parentNode, String expression) {
|
||||
List<SequenceFlow> sequenceFlowList = new ArrayList<>();
|
||||
// 没有子级了
|
||||
if (!NodeUtil.isNode(node)) {
|
||||
return sequenceFlowList;
|
||||
}
|
||||
|
||||
String pid = parentNode.getId();
|
||||
|
||||
if (StrUtil.hasBlank(pid, node.getId())) {
|
||||
return sequenceFlowList;
|
||||
}
|
||||
|
||||
SequenceFlow sequenceFlow = buildSingleSequenceFlow(parentNode.getTailId(), node.getHeadId(), expression);
|
||||
sequenceFlowList.add(sequenceFlow);
|
||||
|
||||
return sequenceFlowList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成扩展数据
|
||||
* @param key
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public static ExtensionAttribute generateExtensionAttribute(String key, String val) {
|
||||
ExtensionAttribute ea = new ExtensionAttribute();
|
||||
|
||||
ea.setName(key);
|
||||
ea.setValue(val);
|
||||
return ea;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建抄送节点
|
||||
* @param node
|
||||
* @return
|
||||
*/
|
||||
private static FlowElement buildCCNode(Node node) {
|
||||
|
||||
ServiceTask serviceTask = new ServiceTask();
|
||||
serviceTask.setId(node.getId());
|
||||
serviceTask.setName(node.getName());
|
||||
serviceTask.setImplementationType("class");
|
||||
serviceTask.setImplementation(CopyServiceTask.class.getCanonicalName());
|
||||
return serviceTask;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建连接线
|
||||
* @param node 父级节点
|
||||
* @param flowId
|
||||
* @return 所有连接线
|
||||
*/
|
||||
private static List<SequenceFlow> buildInnerSequenceFlow(Node node, String flowId) {
|
||||
List<SequenceFlow> sequenceFlowList = new ArrayList<>();
|
||||
|
||||
if (!NodeUtil.isNode(node)) {
|
||||
return sequenceFlowList;
|
||||
}
|
||||
|
||||
String nodeId = node.getId();
|
||||
if (StrUtil.hasBlank(nodeId)) {
|
||||
return sequenceFlowList;
|
||||
}
|
||||
boolean parentIsGateway = false;
|
||||
Node prevNode = null;
|
||||
if (StrUtil.isNotBlank(node.getParentId())) {
|
||||
// 判断父级是否是包容或者并行网关
|
||||
RemoteFlowTaskService remoteFlowTaskService = SpringUtil.getBean(RemoteFlowTaskService.class);
|
||||
prevNode = remoteFlowTaskService.queryNodeOriData(flowId, node.getParentId()).getData();
|
||||
if (prevNode != null) {
|
||||
parentIsGateway = (Objects.equals(prevNode.getType(), NodeTypeEnum.PARALLEL_GATEWAY.getValue()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (parentIsGateway) {
|
||||
String headId = node.getHeadId();
|
||||
node.setHeadId(StrUtil.format("{}_merge_gateway", prevNode.getId()));
|
||||
{
|
||||
SequenceFlow sequenceFlow = buildSingleSequenceFlow(node.getHeadId(), headId, "${12==12}");
|
||||
sequenceFlowList.add(sequenceFlow);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (node.getType() == NodeTypeEnum.APPROVAL.getValue().intValue()) {
|
||||
|
||||
String gatewayId = StrUtil.format("approve_gateway_{}", nodeId);
|
||||
String endId = StrUtil.format("approve_end_{}", nodeId);
|
||||
|
||||
{
|
||||
SequenceFlow sequenceFlow = buildSingleSequenceFlow(nodeId, gatewayId, "${12==12}");
|
||||
sequenceFlowList.add(sequenceFlow);
|
||||
}
|
||||
|
||||
{
|
||||
SequenceFlow sequenceFlow = buildSingleSequenceFlow(gatewayId, endId,
|
||||
StrUtil.format("${!" + "{}_approve_condition}", nodeId));
|
||||
sequenceFlowList.add(sequenceFlow);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return sequenceFlowList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建单个连接线
|
||||
* @param pId 父级id
|
||||
* @param childId 子级id
|
||||
* @param expression 表达式
|
||||
* @return
|
||||
*/
|
||||
private static SequenceFlow buildSingleSequenceFlow(String pId, String childId, String expression) {
|
||||
SequenceFlow sequenceFlow = new SequenceFlow(pId, childId);
|
||||
sequenceFlow.setConditionExpression(expression);
|
||||
sequenceFlow.setName(StrUtil.format("{}|{}", pId, childId));
|
||||
sequenceFlow.setId(StrUtil.format("sq-id-{}-{}", IdUtil.fastSimpleUUID(), RandomUtil.randomInt(1, 10000000)));
|
||||
return sequenceFlow;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
server:
|
||||
port: 5020
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: @artifactId@
|
||||
cloud:
|
||||
nacos:
|
||||
username: @nacos.username@
|
||||
password: @nacos.password@
|
||||
discovery:
|
||||
server-addr: ${NACOS_HOST:pigx-register}:${NACOS_PORT:8848}
|
||||
config:
|
||||
server-addr: ${spring.cloud.nacos.discovery.server-addr}
|
||||
config:
|
||||
import:
|
||||
- optional:nacos:application-@profiles.active@.yml
|
||||
- optional:nacos:${spring.application.name}-@profiles.active@.yml
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
druid:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
username: ${MYSQL_USER:root}
|
||||
password: ${MYSQL_PWD:root}
|
||||
url: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:simple_flow}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&nullCatalogMeansCurrent=true
|
||||
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
小技巧: 在根pom里面设置统一存放路径,统一管理方便维护
|
||||
<properties>
|
||||
<log-path>/Users/lengleng</log-path>
|
||||
</properties>
|
||||
1. 其他模块加日志输出,直接copy本文件放在resources 目录即可
|
||||
2. 注意修改 <property name="${log-path}/log.path" value=""/> 的value模块
|
||||
-->
|
||||
<configuration debug="false" scan="false">
|
||||
<property name="log.path" value="logs/${project.artifactId}"/>
|
||||
<!-- 彩色日志格式 -->
|
||||
<property name="CONSOLE_LOG_PATTERN"
|
||||
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
|
||||
<!-- 彩色日志依赖的渲染类 -->
|
||||
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
|
||||
<conversionRule conversionWord="wex"
|
||||
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
|
||||
<conversionRule conversionWord="wEx"
|
||||
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
|
||||
<!-- Console log output -->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- Log file debug output -->
|
||||
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/debug.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>50MB</maxFileSize>
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- Log file error output -->
|
||||
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/error.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>50MB</maxFileSize>
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>ERROR</level>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<logger name="org.activiti.engine.impl.db" level="DEBUG">
|
||||
<appender-ref ref="debug"/>
|
||||
</logger>
|
||||
|
||||
<!--nacos 心跳 INFO 屏蔽-->
|
||||
<logger name="com.alibaba.nacos" level="OFF">
|
||||
<appender-ref ref="error"/>
|
||||
</logger>
|
||||
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
|
||||
<root level="INFO">
|
||||
<appender-ref ref="console"/>
|
||||
<appender-ref ref="debug"/>
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-flow</artifactId>
|
||||
<version>5.2.0-snapshot</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pigx-flow-engine</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<description>flowable 工作流核心引擎</description>
|
||||
|
||||
<!--项目子模块-->
|
||||
<modules>
|
||||
<module>pigx-flow-engine-api</module>
|
||||
<module>pigx-flow-engine-biz</module>
|
||||
</modules>
|
||||
</project>
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-flow-task</artifactId>
|
||||
<version>5.2.0-snapshot</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pigx-flow-task-api</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<!--core 工具类-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-core</artifactId>
|
||||
</dependency>
|
||||
<!--mybatis plus extension,包含了mybatis plus core-->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-extension</artifactId>
|
||||
</dependency>
|
||||
<!--feign 工具类-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-feign</artifactId>
|
||||
</dependency>
|
||||
<!-- excel 导入导出 -->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-excel</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
package com.pig4cloud.pigx.flow.task.api.feign;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.flow.task.dto.*;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2023/7/14
|
||||
*/
|
||||
@FeignClient(contextId = "remoteFlowEngineService", value = ServiceNameConstants.FLOW_ENGINE_SERVER)
|
||||
public interface RemoteFlowEngineService {
|
||||
|
||||
/**
|
||||
* 查询任务变量
|
||||
* @param variableQueryParamDto 变量查询参数
|
||||
* @return 任务变量结果
|
||||
*/
|
||||
@PostMapping("/task/queryTaskVariables")
|
||||
R<Map<String, Object>> queryTaskVariables(@RequestBody VariableQueryParamDto variableQueryParamDto);
|
||||
|
||||
/**
|
||||
* 查询简单数据
|
||||
* @param userId 用户ID
|
||||
* @return 索引页统计数据
|
||||
*/
|
||||
@GetMapping("/process-instance/querySimpleData")
|
||||
R<IndexPageStatistics> querySimpleData(@RequestParam("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 创建流程
|
||||
* @param map 流程JSON对象
|
||||
* @return 创建流程结果
|
||||
*/
|
||||
@PostMapping("/flow/create")
|
||||
R<String> createFlow(@RequestBody Map<String, Object> map);
|
||||
|
||||
/**
|
||||
* 启动流程实例
|
||||
* @param processInstanceParamDto 流程实例参数
|
||||
* @return 启动流程结果
|
||||
*/
|
||||
@PostMapping("/flow/start")
|
||||
R<String> startProcess(@RequestBody ProcessInstanceParamDto processInstanceParamDto);
|
||||
|
||||
/**
|
||||
* 查询待办任务
|
||||
* @param paramDto 任务查询参数
|
||||
* @return 待办任务列表
|
||||
*/
|
||||
@PostMapping("/flow/queryAssignTask")
|
||||
R<Page<TaskDto>> queryAssignTask(@RequestBody TaskQueryParamDto paramDto);
|
||||
|
||||
/**
|
||||
* 查询已完成任务
|
||||
* @param paramDto 任务查询参数
|
||||
* @return 已完成任务列表
|
||||
*/
|
||||
@PostMapping("/flow/queryCompletedTask")
|
||||
R<Page<TaskDto>> queryCompletedTask(@RequestBody TaskQueryParamDto paramDto);
|
||||
|
||||
/**
|
||||
* 完成任务
|
||||
* @param taskParamDto 任务参数
|
||||
* @return 完成任务结果
|
||||
*/
|
||||
@PostMapping("/task/complete")
|
||||
R<String> completeTask(@RequestBody TaskParamDto taskParamDto);
|
||||
|
||||
/**
|
||||
* 设置任务负责人
|
||||
* @param taskParamDto 任务参数
|
||||
* @return 设置负责人结果
|
||||
*/
|
||||
@PostMapping("/task/setAssignee")
|
||||
R<String> setAssignee(@RequestBody TaskParamDto taskParamDto);
|
||||
|
||||
/**
|
||||
* 停止流程实例
|
||||
* @param taskParamDto 任务参数
|
||||
* @return 停止流程实例结果
|
||||
*/
|
||||
@PostMapping("/flow/stopProcessInstance")
|
||||
R<String> stopProcessInstance(@RequestBody TaskParamDto taskParamDto);
|
||||
|
||||
/**
|
||||
* 解决任务
|
||||
* @param taskParamDto 任务参数
|
||||
* @return 解决任务结果
|
||||
*/
|
||||
@PostMapping("/task/resolveTask")
|
||||
R<String> resolveTask(@RequestBody TaskParamDto taskParamDto);
|
||||
|
||||
/**
|
||||
* 退回任务
|
||||
* @param taskParamDto 任务参数
|
||||
* @return 退回任务结果
|
||||
*/
|
||||
@PostMapping("/task/back")
|
||||
R<String> back(@RequestBody TaskParamDto taskParamDto);
|
||||
|
||||
/**
|
||||
* 委派任务
|
||||
* @param taskParamDto 任务参数
|
||||
* @return 委派任务结果
|
||||
*/
|
||||
@PostMapping("/task/delegateTask")
|
||||
R<String> delegateTask(@RequestBody TaskParamDto taskParamDto);
|
||||
|
||||
/**
|
||||
* 显示流程图
|
||||
* @param procInsId 流程实例ID
|
||||
* @return 流程图地址
|
||||
*/
|
||||
@GetMapping("/flow/showImg")
|
||||
R<String> showImg(@RequestParam("procInsId") String procInsId);
|
||||
|
||||
/**
|
||||
* 查询任务
|
||||
* @param taskId 任务ID
|
||||
* @param userId 用户ID
|
||||
* @return 任务查询结果
|
||||
*/
|
||||
@GetMapping("/task/queryTask")
|
||||
R<TaskResultDto> queryTask(@RequestParam("taskId") String taskId, @RequestParam("userId") Long userId);
|
||||
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
package com.pig4cloud.pigx.flow.task.api.feign;
|
||||
|
||||
import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.flow.task.dto.*;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2023/7/14
|
||||
*/
|
||||
@FeignClient(contextId = "remoteFlowTaskService", value = ServiceNameConstants.FLOW_TASK_SERVER)
|
||||
public interface RemoteFlowTaskService {
|
||||
|
||||
/**
|
||||
* 节点开始事件
|
||||
* @param nodeRecordParamDto
|
||||
*/
|
||||
@PostMapping("/remote/startNodeEvent")
|
||||
void startNodeEvent(@RequestBody ProcessNodeRecordParamDto nodeRecordParamDto);
|
||||
|
||||
/**
|
||||
* 节点结束事件
|
||||
* @param nodeRecordParamDto
|
||||
*/
|
||||
@PostMapping("/remote/endNodeEvent")
|
||||
void endNodeEvent(@RequestBody ProcessNodeRecordParamDto nodeRecordParamDto);
|
||||
|
||||
/**
|
||||
* 流程结束事件
|
||||
* @param processInstanceParamDto
|
||||
*/
|
||||
@PostMapping("/remote/endProcess")
|
||||
void endProcessEvent(@RequestBody ProcessInstanceParamDto processInstanceParamDto);
|
||||
|
||||
/**
|
||||
* 创建流程事件
|
||||
* @param processInstanceRecordParamDto
|
||||
*/
|
||||
@PostMapping("/remote/createProcessEvent")
|
||||
void createProcessEvent(@RequestBody ProcessInstanceRecordParamDto processInstanceRecordParamDto);
|
||||
|
||||
/**
|
||||
* 根据角色id集合查询用户id集合
|
||||
* @param roleIdList
|
||||
*/
|
||||
@PostMapping("/remote/queryUserIdListByRoleIdList")
|
||||
R<List<String>> queryUserIdListByRoleIdList(@RequestBody List<String> roleIdList);
|
||||
|
||||
/**
|
||||
* 根据部门id集合查询所有的用户id集合
|
||||
* @param deptIdList
|
||||
*/
|
||||
@PostMapping("/remote/queryUserIdListByDepIdList")
|
||||
R<List<String>> queryUserIdListByDepIdList(@RequestBody List<String> deptIdList);
|
||||
|
||||
/**
|
||||
* 查询流程管理员
|
||||
* @param flowId 流程id
|
||||
*/
|
||||
@GetMapping("/remote/queryProcessAdmin")
|
||||
R<Long> queryProcessAdmin(@RequestParam("flowId") String flowId);
|
||||
|
||||
/**
|
||||
* 查询节点数据
|
||||
* @param flowId 流程id
|
||||
* @param nodeId 节点id
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/processNodeData/getNodeData")
|
||||
R<Node> queryNodeOriData(@RequestParam("flowId") String flowId, @RequestParam("nodeId") String nodeId);
|
||||
|
||||
/**
|
||||
* 节点开始指派用户了
|
||||
* @param processNodeRecordAssignUserParamDto
|
||||
*/
|
||||
@PostMapping("/remote/startAssignUser")
|
||||
R startAssignUser(@RequestBody ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto);
|
||||
|
||||
/**
|
||||
* 任务结束事件
|
||||
* @param processNodeRecordAssignUserParamDto
|
||||
*/
|
||||
@PostMapping("/remote/taskEndEvent")
|
||||
R taskEndEvent(@RequestBody ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto);
|
||||
|
||||
/**
|
||||
* 保存抄送数据
|
||||
* @param processCopyDto
|
||||
*/
|
||||
@PostMapping("/remote/savecc")
|
||||
R saveCC(@RequestBody ProcessCopyDto processCopyDto);
|
||||
|
||||
/**
|
||||
* 保存节点原始数据
|
||||
* @param processNodeDataDto
|
||||
*/
|
||||
@PostMapping("/processNodeData/saveNodeData")
|
||||
R saveNodeOriData(@RequestBody ProcessNodeDataDto processNodeDataDto);
|
||||
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
@author pigx archetype
|
||||
* <p>
|
||||
* feign client 存放目录,注意 @EnablePigxFeignClients 的扫描范围
|
||||
*/
|
||||
package com.pig4cloud.pigx.flow.task.api.feign;
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.pig4cloud.pigx.flow.task.constant;
|
||||
|
||||
/**
|
||||
* EasyCaptcha 验证码类型枚举
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2023/03/24
|
||||
*/
|
||||
public enum CaptchaTypeEnum {
|
||||
|
||||
/**
|
||||
* 算数
|
||||
*/
|
||||
ARITHMETIC,
|
||||
/**
|
||||
* 中文
|
||||
*/
|
||||
CHINESE,
|
||||
/**
|
||||
* 中文闪图
|
||||
*/
|
||||
CHINESE_GIF,
|
||||
/**
|
||||
* 闪图
|
||||
*/
|
||||
GIF, SPEC
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.pig4cloud.pigx.flow.task.constant;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* 表单类型枚举
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum FormTypeEnum {
|
||||
|
||||
INPUT("Input", "单行文本", ""), TEXTAREA("Textarea", "多行文本", ""), NUMBER("Number", "数字", null),
|
||||
DATE("Date", "日期", null), DATE_TIME("DateTime", "日期时间", null),
|
||||
|
||||
SEQUENCE("Sequence", "发号器", null), LAYOUT("Layout", "明细", null), TIME("Time", "时间", null),
|
||||
MONEY("Money", "金额", null), SINGLE_SELECT("SingleSelect", "单选", new ArrayList<>()),
|
||||
SELECT_DEPT("SelectDept", "部门", new ArrayList<>()), SELECT_USER("SelectUser", "用户", new ArrayList<>()),
|
||||
|
||||
;
|
||||
|
||||
private String type;
|
||||
|
||||
private String name;
|
||||
|
||||
private Object defaultValue;
|
||||
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package com.pig4cloud.pigx.flow.task.constant;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 枚举通用接口
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2022/3/27 12:06
|
||||
*/
|
||||
public interface IBaseEnum<T> {
|
||||
|
||||
T getValue();
|
||||
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* 根据值获取枚举
|
||||
* @param value
|
||||
* @param clazz
|
||||
* @param <E> 枚举
|
||||
* @return
|
||||
*/
|
||||
static <E extends Enum<E> & IBaseEnum> E getEnumByValue(Object value, Class<E> clazz) {
|
||||
Objects.requireNonNull(value);
|
||||
EnumSet<E> allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举
|
||||
E matchEnum = allEnums.stream().filter(e -> ObjectUtil.equal(e.getValue(), value)).findFirst().orElse(null);
|
||||
return matchEnum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文本标签获取值
|
||||
* @param value
|
||||
* @param clazz
|
||||
* @param <E>
|
||||
* @return
|
||||
*/
|
||||
static <E extends Enum<E> & IBaseEnum> String getLabelByValue(Object value, Class<E> clazz) {
|
||||
Objects.requireNonNull(value);
|
||||
EnumSet<E> allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举
|
||||
E matchEnum = allEnums.stream().filter(e -> ObjectUtil.equal(e.getValue(), value)).findFirst().orElse(null);
|
||||
|
||||
String label = null;
|
||||
if (matchEnum != null) {
|
||||
label = matchEnum.getLabel();
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文本标签获取值
|
||||
* @param label
|
||||
* @param clazz
|
||||
* @param <E>
|
||||
* @return
|
||||
*/
|
||||
static <E extends Enum<E> & IBaseEnum> Object getValueByLabel(String label, Class<E> clazz) {
|
||||
Objects.requireNonNull(label);
|
||||
EnumSet<E> allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举
|
||||
String finalLabel = label;
|
||||
E matchEnum = allEnums.stream()
|
||||
.filter(e -> ObjectUtil.equal(e.getLabel(), finalLabel))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
Object value = null;
|
||||
if (matchEnum != null) {
|
||||
value = matchEnum.getValue();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.pig4cloud.pigx.flow.task.constant;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 菜单类型枚举
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2022/4/23 9:36
|
||||
*/
|
||||
|
||||
public enum MenuTypeEnum implements IBaseEnum<Integer> {
|
||||
|
||||
NULL(0, null), MENU(1, "菜单"), CATALOG(2, "目录"), EXTLINK(3, "外链"), BUTTON(4, "按钮");
|
||||
|
||||
@Getter
|
||||
@EnumValue // Mybatis-Plus 提供注解表示插入数据库时插入该值
|
||||
private Integer value;
|
||||
|
||||
@Getter
|
||||
// @JsonValue // 表示对枚举序列化时返回此字段
|
||||
private String label;
|
||||
|
||||
MenuTypeEnum(Integer value, String label) {
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.pig4cloud.pigx.flow.task.constant;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum NodeStatusEnum {
|
||||
|
||||
WKS(0, "未开始"), JXZ(1, "进行中"), YJS(2, "已结束"),;
|
||||
|
||||
private int code;
|
||||
|
||||
private String name;
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.pig4cloud.pigx.flow.task.constant;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 节点枚举
|
||||
*/
|
||||
@Getter
|
||||
public enum NodeTypeEnum {
|
||||
|
||||
ROOT("根节点", 0),
|
||||
|
||||
APPROVAL("审批节点", 1), CC("抄送节点", 2), EXCLUSIVE_GATEWAY("条件分支", 4), PARALLEL_GATEWAY("并行分支", 5), EMPTY("空", 3)
|
||||
|
||||
;
|
||||
|
||||
NodeTypeEnum(String name, Integer value) {
|
||||
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
private String name;
|
||||
|
||||
private Integer value;
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.pig4cloud.pigx.flow.task.constant;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 用户节点类型枚举
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum NodeUserTypeEnum {
|
||||
|
||||
USER("user", "用户"), DEPT("dept", "部门"), ROLE("role", "角色"),;
|
||||
|
||||
private String key;
|
||||
|
||||
private String name;
|
||||
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package com.pig4cloud.pigx.flow.task.constant;
|
||||
|
||||
public class ProcessInstanceConstant {
|
||||
|
||||
/**
|
||||
* 空执行人
|
||||
*/
|
||||
public static final Long DEFAULT_EMPTY_ASSIGN = -99999999L;
|
||||
|
||||
/**
|
||||
* 用户任务没有执行人的情况下如何处理 自动通过
|
||||
*/
|
||||
public static final String USER_TASK_NOBODY_HANDLER_TO_PASS = "TO_PASS";
|
||||
|
||||
/**
|
||||
* 转交给管理员
|
||||
*/
|
||||
public static final String USER_TASK_NOBODY_HANDLER_TO_ADMIN = "TO_ADMIN";
|
||||
|
||||
/**
|
||||
* 指定人员
|
||||
*/
|
||||
public static final String USER_TASK_NOBODY_HANDLER_TO_USER = "TO_USER";
|
||||
|
||||
/**
|
||||
* 结束流程
|
||||
*/
|
||||
public static final String USER_TASK_NOBODY_HANDLER_TO_END = "TO_END";
|
||||
|
||||
/**
|
||||
* 拒绝之后 结束流程
|
||||
*/
|
||||
public static final String USER_TASK_REFUSE_TYPE_TO_END = "TO_END";
|
||||
|
||||
/**
|
||||
* 拒绝之后 到某个节点
|
||||
*/
|
||||
public static final String USER_TASK_REFUSE_TYPE_TO_NODE = "TO_NODE";
|
||||
|
||||
/**
|
||||
* 会签
|
||||
*/
|
||||
public static final int MULTIPLE_MODE_AL_SAME = 1;
|
||||
|
||||
/**
|
||||
* 或签
|
||||
*/
|
||||
public static final int MULTIPLE_MODE_ONE = 2;
|
||||
|
||||
/**
|
||||
* 顺签
|
||||
*/
|
||||
public static final int MULTIPLE_MODE_ALL_SORT = 3;
|
||||
|
||||
public static class AssignedTypeClass {
|
||||
|
||||
// 指定用户
|
||||
public static final int USER = 1;
|
||||
|
||||
// 发起人自己
|
||||
public static final int SELF = 5;
|
||||
|
||||
// 表单人员
|
||||
public static final int FORM_USER = 8;
|
||||
|
||||
// 发起人自选
|
||||
public static final int SELF_SELECT = 4;
|
||||
|
||||
// 角色
|
||||
public static final int ROLE = 3;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单权限
|
||||
*/
|
||||
public static class FormPermClass {
|
||||
|
||||
// 隐藏
|
||||
public static final String HIDE = "H";
|
||||
|
||||
// 只读
|
||||
public static final String READ = "R";
|
||||
|
||||
// 编辑
|
||||
public static final String EDIT = "E";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package com.pig4cloud.pigx.flow.task.constant;
|
||||
|
||||
/**
|
||||
* Security 常量
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2023/03/24
|
||||
*/
|
||||
public interface SecurityConstants {
|
||||
|
||||
/**
|
||||
* 登录接口路径
|
||||
*/
|
||||
String LOGIN_PATH = "/api/v1/auth/login";
|
||||
|
||||
/**
|
||||
* Token 前缀
|
||||
*/
|
||||
String TOKEN_PREFIX = "Bearer ";
|
||||
|
||||
/**
|
||||
* 请求头Token的Key
|
||||
*/
|
||||
String TOKEN_KEY = "Authorization";
|
||||
|
||||
/**
|
||||
* 验证码缓存前缀
|
||||
*/
|
||||
String VERIFY_CODE_CACHE_PREFIX = "AUTH:VERIFY_CODE:";
|
||||
|
||||
/**
|
||||
* 用户权限集合缓存前缀
|
||||
*/
|
||||
String USER_PERMS_CACHE_PREFIX = "AUTH:USER_PERMS:";
|
||||
|
||||
/**
|
||||
* 黑名单Token缓存前缀
|
||||
*/
|
||||
String BLACK_TOKEN_CACHE_PREFIX = "AUTH:BLACK_TOKEN:";
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.pig4cloud.pigx.flow.task.constant;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 状态枚举
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2022/10/14
|
||||
*/
|
||||
public enum StatusEnum implements IBaseEnum<Integer> {
|
||||
|
||||
ENABLE(1, "启用"), DISABLE(0, "禁用");
|
||||
|
||||
@Getter
|
||||
private Integer value;
|
||||
|
||||
@Getter
|
||||
private String label;
|
||||
|
||||
StatusEnum(Integer value, String label) {
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.pig4cloud.pigx.flow.task.constant;
|
||||
|
||||
/**
|
||||
* 系统常量
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2022/10/22
|
||||
*/
|
||||
public interface SystemConstants {
|
||||
|
||||
/**
|
||||
* 根节点ID
|
||||
*/
|
||||
Long ROOT_NODE_ID = 0L;
|
||||
|
||||
/**
|
||||
* 系统默认密码
|
||||
*/
|
||||
String DEFAULT_PASSWORD = "123456";
|
||||
|
||||
/**
|
||||
* 超级管理员角色编码
|
||||
*/
|
||||
String ROOT_ROLE_CODE = "ROOT";
|
||||
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* @author pigx archetype
|
||||
* <p>
|
||||
* 常量和枚举定义
|
||||
*/
|
||||
package com.pig4cloud.pigx.flow.task.constant;
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.pig4cloud.pigx.flow.task.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 检查子部门DTO
|
||||
*/
|
||||
@Data
|
||||
public class CheckChildDto {
|
||||
|
||||
/**
|
||||
* 子部门ID
|
||||
*/
|
||||
private Long childId;
|
||||
|
||||
/**
|
||||
* 父部门ID列表
|
||||
*/
|
||||
private List<Long> deptIdList;
|
||||
|
||||
}
|
||||
Loaded 100 of 256 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user