diff --git a/doc/db/2pigxx.sql b/doc/db/2pigxx.sql index 5dbac2092..0106a37f0 100644 --- a/doc/db/2pigxx.sql +++ b/doc/db/2pigxx.sql @@ -276,7 +276,7 @@ CREATE TABLE `sys_route_conf` ( -- Records of `sys_route_conf` -- ---------------------------- BEGIN; -INSERT INTO `sys_route_conf` VALUES ('1', '工作流管理模块', 'pigx-activiti', '[{\"args\": {\"_genkey_0\": \"/act/**\"}, \"name\": \"Path\"}]', '[]', 'lb://pigx-activiti', '0', '2018-11-28 15:56:10', '2018-11-28 15:56:25', '0'), ('2', '认证中心', 'pigx-auth', '[{\"args\": {\"_genkey_0\": \"/auth/**\"}, \"name\": \"Path\"}]', '[{\"args\": {}, \"name\": \"ValidateCodeGatewayFilter\"}, {\"args\": {}, \"name\": \"PasswordDecoderFilter\"}]', 'lb://pigx-auth', '0', '2018-11-28 15:56:10', '2018-11-28 15:56:29', '0'), ('3', '代码生成模块', 'pigx-codegen', '[{\"args\": {\"_genkey_0\": \"/gen/**\"}, \"name\": \"Path\"}]', '[]', 'lb://pigx-codegen', '0', '2018-11-28 15:56:10', '2018-11-28 15:56:32', '0'), ('4', '定时任务模块', 'pigx-daemon', '[{\"args\": {\"_genkey_0\": \"/daemon/**\"}, \"name\": \"Path\"}]', '[]', 'lb://pigx-daemon', '0', '2018-11-28 15:56:10', '2018-11-28 15:56:34', '0'), ('5', '分布式事务模块', 'pigx-tx-manager', '[{\"args\": {\"_genkey_0\": \"/tx/**\"}, \"name\": \"Path\"}]', '[]', 'lb://pigx-tx-manager', '0', '2018-11-28 15:56:10', '2018-11-28 15:56:36', '0'), ('6', '通用权限模块', 'pigx-upms-biz', '[{\"args\": {\"_genkey_0\": \"/admin/**\"}, \"name\": \"Path\"}]', '[{\"args\": {\"key-resolver\": \"#{@remoteAddrKeyResolver}\", \"redis-rate-limiter.burstCapacity\": \"20\", \"redis-rate-limiter.replenishRate\": \"10\"}, \"name\": \"RequestRateLimiter\"}, {\"args\": {\"name\": \"default\", \"fallbackUri\": \"forward:/fallback\"}, \"name\": \"Hystrix\"}]', 'lb://pigx-upms-biz', '0', '2018-11-28 15:56:10', '2018-11-28 15:56:39', '0'); +INSERT INTO `sys_route_conf` VALUES ('1', '工作流管理模块', 'pigx-activiti', '[{\"args\": {\"_genkey_0\": \"/act/**\"}, \"name\": \"Path\"}]', '[]', 'lb://pigx-activiti', '0', '2018-11-28 15:56:10', '2018-11-28 15:56:25', '0'), ('2', '认证中心', 'pigx-auth', '[{\"args\": {\"_genkey_0\": \"/auth/**\"}, \"name\": \"Path\"}]', '[{\"args\": {}, \"name\": \"ValidateCodeGatewayFilter\"}, {\"args\": {}, \"name\": \"PasswordDecoderFilter\"}]', 'lb://pigx-auth', '0', '2018-11-28 15:56:10', '2018-11-28 15:56:29', '0'), ('3', '代码生成模块', 'pigx-codegen', '[{\"args\": {\"_genkey_0\": \"/gen/**\"}, \"name\": \"Path\"}]', '[]', 'lb://pigx-codegen', '0', '2018-11-28 15:56:10', '2018-11-28 15:56:32', '0'), ('4', '定时任务模块', 'pigx-daemon', '[{\"args\": {\"_genkey_0\": \"/daemon/**\"}, \"name\": \"Path\"}]', '[]', 'lb://pigx-daemon', '0', '2018-11-28 15:56:10', '2018-11-28 15:56:34', '0'), ('5', '分布式事务模块', 'pigx-tx-manager', '[{\"args\": {\"_genkey_0\": \"/tx/**\"}, \"name\": \"Path\"}]', '[]', 'lb://pigx-tx-manager', '0', '2018-11-28 15:56:10', '2018-11-28 15:56:36', '0'), ('6', '通用权限模块', 'pigx-upms-biz', '[{\"args\": {\"_genkey_0\": \"/admin/**\"}, \"name\": \"Path\"}]', '[{\"args\": {\"key-resolver\": \"#{@remoteAddrKeyResolver}\", \"redis-rate-limiter.burstCapacity\": \"20\", \"redis-rate-limiter.replenishRate\": \"10\"}, \"name\": \"RequestRateLimiter\"}, {\"args\": {\"name\": \"default\", \"fallbackUri\": \"forward:/fallback\"}, \"name\": \"Hystrix\"}]', 'lb://pigx-upms-biz', '0', '2018-11-28 15:56:10', '2018-11-28 15:56:39', '0'), ('7', '认证中心长连接模块1', 'pigx-auth-ws-1', '[{\"args\": {\"_genkey_0\": \"/auth/ws/info\"}, \"name\": \"Path\"}]', '[]', 'lb://pigx-auth', '0', '2019-01-15 19:37:30', '2019-01-16 20:54:47', '0'), ('8', '认证中心长连接模块2', 'pigx-auth-ws-2', '[{\"args\": {\"_genkey_0\": \"/auth/ws/**\"}, \"name\": \"Path\"}]', '[]', 'lb:ws://pigx-auth', '0', '2019-01-15 19:22:39', '2019-01-16 20:54:51', '0'), ('9', '工作流长链接支持1', 'pigx-activiti-ws-1', '[{\"args\": {\"_genkey_0\": \"/act/ws/**\"}, \"name\": \"Path\"}]', '[]', 'lb:ws://pigx-activiti', '0', '2019-01-15 19:22:39', '2019-01-16 20:54:56', '0'), ('10', '工作流长链接支持2', 'pigx-activiti-ws-2', '[{\"args\": {\"_genkey_0\": \"/act/ws/info\"}, \"name\": \"Path\"}]', '[]', 'lb://pigx-activiti', '0', '2019-01-15 19:37:30', '2019-01-16 20:55:07', '0'); COMMIT; -- ---------------------------- diff --git a/pigx-config/src/main/resources/config/pigx-activiti-dev.yml b/pigx-config/src/main/resources/config/pigx-activiti-dev.yml index bc971188c..ca34ffc90 100644 --- a/pigx-config/src/main/resources/config/pigx-activiti-dev.yml +++ b/pigx-config/src/main/resources/config/pigx-activiti-dev.yml @@ -13,6 +13,7 @@ security: - '/task/view/*' - '/process/resource/**' - '/modeler.html' + - '/ws/**' spring: autoconfigure: exclude: org.activiti.spring.boot.SecurityAutoConfiguration diff --git a/pigx-visual/pigx-activiti/pom.xml b/pigx-visual/pigx-activiti/pom.xml index b1ce944d3..d13a5902f 100644 --- a/pigx-visual/pigx-activiti/pom.xml +++ b/pigx-visual/pigx-activiti/pom.xml @@ -65,6 +65,11 @@ pigx-common-swagger 2.1.2 + + + org.springframework.boot + spring-boot-starter-websocket + org.activiti diff --git a/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/config/ActivitiConfig.java b/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/config/ActivitiConfig.java index 5034827fd..d3e35deb3 100644 --- a/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/config/ActivitiConfig.java +++ b/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/config/ActivitiConfig.java @@ -21,6 +21,9 @@ import lombok.AllArgsConstructor; import org.activiti.spring.SpringProcessEngineConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.task.TaskExecutor; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; @@ -39,7 +42,7 @@ public class ActivitiConfig { @Bean public SpringProcessEngineConfiguration getProcessEngineConfiguration() { SpringProcessEngineConfiguration config = - new SpringProcessEngineConfiguration(); + new SpringProcessEngineConfiguration(); // 流程图字体设置 config.setActivityFontName("宋体"); config.setAnnotationFontName("宋体"); @@ -51,4 +54,10 @@ public class ActivitiConfig { config.setDatabaseSchemaUpdate("true"); return config; } + + @Bean + @Primary + public TaskExecutor primaryTaskExecutor() { + return new ThreadPoolTaskExecutor(); + } } diff --git a/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/config/WebSocketConfig.java b/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/config/WebSocketConfig.java new file mode 100644 index 000000000..012ec28cb --- /dev/null +++ b/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/config/WebSocketConfig.java @@ -0,0 +1,66 @@ +package com.pig4cloud.pigx.act.config; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.simp.config.ChannelRegistration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.messaging.simp.stomp.StompCommand; +import org.springframework.messaging.simp.stomp.StompHeaderAccessor; +import org.springframework.messaging.support.ChannelInterceptor; +import org.springframework.messaging.support.MessageHeaderAccessor; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.RemoteTokenServices; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +/** + * @author new + *

+ * WebSocket配置类 + */ +@Slf4j +@Configuration +@AllArgsConstructor +@EnableWebSocketMessageBroker +public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + private RemoteTokenServices tokenService; + + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + registry.addEndpoint("/ws") + .setAllowedOrigins("*") + .withSockJS(); + } + + @Override + public void configureMessageBroker(MessageBrokerRegistry registry) { + registry.setUserDestinationPrefix("/task/"); + } + + @Override + public void configureClientInboundChannel(ChannelRegistration registration) { + registration.interceptors(new ChannelInterceptor() { + @Override + public Message preSend(Message message, MessageChannel channel) { + StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); + if (StompCommand.CONNECT.equals(accessor.getCommand())) { + String tokens = accessor.getFirstNativeHeader("Authorization"); + log.info("webSocket token is {}", tokens); + if (StrUtil.isBlank(tokens)) { + return null; + } + OAuth2Authentication auth2Authentication = tokenService.loadAuthentication(tokens.split(" ")[1]); + SecurityContextHolder.getContext().setAuthentication(auth2Authentication); + accessor.setUser(() -> (String) auth2Authentication.getPrincipal()); + } + return message; + } + }); + } +} diff --git a/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/listener/LeaveProcessTaskListener.java b/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/listener/LeaveProcessTaskListener.java index 0e2addf43..012b27613 100644 --- a/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/listener/LeaveProcessTaskListener.java +++ b/pigx-visual/pigx-activiti/src/main/java/com/pig4cloud/pigx/act/listener/LeaveProcessTaskListener.java @@ -22,11 +22,14 @@ 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.common.core.util.SpringContextHolder; +import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder; import com.pig4cloud.pigx.common.security.util.SecurityUtils; import lombok.extern.slf4j.Slf4j; import org.activiti.engine.delegate.DelegateTask; import org.activiti.engine.delegate.TaskListener; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -45,18 +48,26 @@ public class LeaveProcessTaskListener implements TaskListener { */ @Override public void notify(DelegateTask delegateTask) { + SimpMessagingTemplate simpMessagingTemplate = SpringContextHolder.getBean(SimpMessagingTemplate.class); RemoteUserService userService = SpringContextHolder.getBean(RemoteUserService.class); R> result = userService.ancestorUsers(SecurityUtils.getUsername()); + List remindUserList = new ArrayList<>(); if (CollUtil.isEmpty(result.getData())) { log.info("用户 {} 不存在上级,任务单由当前用户审批", SecurityUtils.getUsername()); delegateTask.addCandidateUser(SecurityUtils.getUsername()); + remindUserList.add(SecurityUtils.getUsername()); } else { List userList = result.getData().stream().map(SysUser::getUsername).collect(Collectors.toList()); log.info("当前任务 {},由 {}处理", delegateTask.getId(), userList); delegateTask.addCandidateUsers(userList); + remindUserList.addAll(userList); } + remindUserList.forEach(user -> { + String target = String.format("%s-%s", SecurityUtils.getUsername(), TenantContextHolder.getTenantId()); + simpMessagingTemplate.convertAndSendToUser(target, "/remind", delegateTask.getName()); + }); } }