diff --git a/snow-admin/src/main/java/com/snow/web/controller/system/SysMessageCenterController.java b/snow-admin/src/main/java/com/snow/web/controller/system/SysMessageCenterController.java index a08e817..a7ad041 100644 --- a/snow-admin/src/main/java/com/snow/web/controller/system/SysMessageCenterController.java +++ b/snow-admin/src/main/java/com/snow/web/controller/system/SysMessageCenterController.java @@ -62,6 +62,13 @@ public class SysMessageCenterController extends BaseController mmap.put("emailList",emailList); } + if(CollectionUtil.isNotEmpty(sysMessageTransitions)){ + List todoTaskList = sysMessageTransitions.stream().filter(t -> t.getMessageType().equals(MessageEventType.INNER_TASK_TODO.getCode())).collect(Collectors.toList()); + long count = todoTaskList.stream().filter(t -> t.getMessageReadStatus() == 0).count(); + mmap.put("todoTaskCount",count); + mmap.put("todoTaskList",todoTaskList); + } + return prefix + "/messageCenter"; } diff --git a/snow-admin/src/main/resources/templates/system/messageCenter/messageCenter.html b/snow-admin/src/main/resources/templates/system/messageCenter/messageCenter.html index ab3e327..80faf98 100644 --- a/snow-admin/src/main/resources/templates/system/messageCenter/messageCenter.html +++ b/snow-admin/src/main/resources/templates/system/messageCenter/messageCenter.html @@ -19,7 +19,8 @@ 站内邮件 [[${emailListCount}]] -
  • 第二个选项卡 +
  • + 系统流程待办 [[${todoTaskCount}]]
  • 第二个选项卡
  • @@ -71,7 +72,7 @@

    查看详情 @@ -86,10 +87,30 @@
    - 栅格系统 + 系统流程待办 +
    +
    +
    + +
    -

    Bootstrap 提供了一套响应式、移动设备优先的流式栅格系统,随着屏幕或视口(viewport)尺寸的增加,系统会自动分为最多12列。它包含了易于使用的预定义类,还有强大的mixin 用于生成更具语义的布局。

    -
    +
    +

    + + +

    +

    [[${todoTask.messageContent}]]

    + 查看详情 +

    + [[${email.spendTime}]] +

    +
    +
    +
    +
    diff --git a/snow-common/src/main/java/com/snow/common/constant/MessageConstants.java b/snow-common/src/main/java/com/snow/common/constant/MessageConstants.java index 012917b..93fa6a0 100644 --- a/snow-common/src/main/java/com/snow/common/constant/MessageConstants.java +++ b/snow-common/src/main/java/com/snow/common/constant/MessageConstants.java @@ -37,4 +37,10 @@ public class MessageConstants { */ public static final String CUSTOMER_VISIT_LOG_CODE="1411568525448978432"; + /** + * 站内信任务创建模板 + */ + public static final String INNER_TASK_CREATED_CODE="1415927384573616128"; + + } diff --git a/snow-common/src/main/java/com/snow/common/enums/MessageEventType.java b/snow-common/src/main/java/com/snow/common/enums/MessageEventType.java index 5ebbab2..b954ddc 100644 --- a/snow-common/src/main/java/com/snow/common/enums/MessageEventType.java +++ b/snow-common/src/main/java/com/snow/common/enums/MessageEventType.java @@ -9,6 +9,8 @@ package com.snow.common.enums; public enum MessageEventType { TASK_TODO("TASK_TODO", "任务待办"), + INNER_TASK_TODO("INNER_TASK_TODO", "站内信任务待办"), + TASK_FINISH("TASK_FINISH", "任务完成"), SEND_EMAIL("SEND_EMAIL", "发送邮件"), diff --git a/snow-flowable/src/main/java/com/snow/flowable/listener/common/GlobalListenerConfig.java b/snow-flowable/src/main/java/com/snow/flowable/listener/common/GlobalListenerConfig.java new file mode 100644 index 0000000..742bdc9 --- /dev/null +++ b/snow-flowable/src/main/java/com/snow/flowable/listener/common/GlobalListenerConfig.java @@ -0,0 +1,33 @@ +package com.snow.flowable.listener.common; + +import lombok.RequiredArgsConstructor; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.lang.NonNull; + +/** + * Flowable全局监听器配置 + * + */ +@Configuration +@RequiredArgsConstructor +public class GlobalListenerConfig implements ApplicationListener { + + private final SpringProcessEngineConfiguration configuration; + + private final TaskCreateListener taskCreateListener; + + @Override + public void onApplicationEvent(@NonNull ContextRefreshedEvent event) { + FlowableEventDispatcher dispatcher = configuration.getEventDispatcher(); + // 流程结束事件 + // dispatcher.addEventListener(processEndListener, FlowableEngineEventType.PROCESS_COMPLETED); + //任务创建 + dispatcher.addEventListener(taskCreateListener, FlowableEngineEventType.TASK_CREATED); + } + +} diff --git a/snow-flowable/src/main/java/com/snow/flowable/listener/common/ProcessEndListener.java b/snow-flowable/src/main/java/com/snow/flowable/listener/common/ProcessEndListener.java new file mode 100644 index 0000000..4fc08b8 --- /dev/null +++ b/snow-flowable/src/main/java/com/snow/flowable/listener/common/ProcessEndListener.java @@ -0,0 +1,85 @@ +package com.snow.flowable.listener.common; + +import cn.hutool.core.util.ObjectUtil; +import com.snow.flowable.common.enums.FlowDefEnum; +import com.snow.flowable.service.FlowableService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEvent; +import org.flowable.common.engine.api.delegate.event.FlowableEvent; +import org.flowable.common.engine.api.delegate.event.FlowableEventListener; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Optional; + +/** + * @author qimingjin + * @Title: + * @Description: + * @date 2021/7/16 13:29 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class ProcessEndListener implements FlowableEventListener { + + @Resource + private FlowableService flowableService; + + @Override + public void onEvent(FlowableEvent flowableEvent) { + if (flowableEvent instanceof FlowableEngineEvent) { + FlowableEngineEvent flowableEngineEvent = (FlowableEngineEvent) flowableEvent; + ProcessDefinitionEntity processDefinition = flowableService.getProcessDefinition(flowableEngineEvent); + if(ObjectUtil.isNull(processDefinition)){ + return; + } + String key = Optional.ofNullable(processDefinition.getKey()).orElse(""); + for (FlowDefEnum flowDefEnum : flowableService.getAllFlowDefEnumsSet()) { + //在流程中存在的才监听 + if (flowDefEnum.getCode().equals(key)) { + //发送站内信 + } + } + + + } + + } + + @Override + public boolean isFailOnException() { + return false; + } + + @Override + public boolean isFireOnTransactionLifecycleEvent() { + return false; + } + + @Override + public String getOnTransaction() { + return null; + } + + /** + * 获取流程定义信息 + * + * @param event + * @return + */ + protected ProcessDefinitionEntity getProcessDefinition(FlowableEngineEvent event) { + String processDefinitionId = event.getProcessDefinitionId(); + if (processDefinitionId != null) { + CommandContext commandContext = CommandContextUtil.getCommandContext(); + if (commandContext != null) { + return CommandContextUtil.getProcessDefinitionEntityManager(commandContext).findById(processDefinitionId); + } + } + return null; + } +} diff --git a/snow-flowable/src/main/java/com/snow/flowable/listener/common/TaskCreateListener.java b/snow-flowable/src/main/java/com/snow/flowable/listener/common/TaskCreateListener.java new file mode 100644 index 0000000..969229a --- /dev/null +++ b/snow-flowable/src/main/java/com/snow/flowable/listener/common/TaskCreateListener.java @@ -0,0 +1,122 @@ +package com.snow.flowable.listener.common; + +import cn.hutool.core.util.ObjectUtil; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.snow.common.constant.MessageConstants; +import com.snow.common.enums.MessageEventType; +import com.snow.flowable.common.enums.FlowDefEnum; +import com.snow.flowable.service.FlowableService; +import com.snow.flowable.service.impl.FlowableUserServiceImpl; +import com.snow.framework.web.domain.common.MessageEventDTO; +import com.snow.system.domain.SysUser; +import com.snow.system.service.impl.SysUserServiceImpl; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; +import org.flowable.common.engine.api.delegate.event.FlowableEvent; +import org.flowable.common.engine.api.delegate.event.FlowableEventListener; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity; +import org.flowable.task.service.impl.persistence.entity.TaskEntity; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * @author qimingjin + * @Title: 待办创建 + * @Description: + * @date 2021/7/16 13:29 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class TaskCreateListener implements FlowableEventListener { + + @Resource + private FlowableService flowableService; + + @Resource + private FlowableUserServiceImpl flowableUserService; + + @Resource + private SysUserServiceImpl sysUserServiceImpl; + + + @Resource + private ApplicationContext applicationContext; + + @Override + public void onEvent(FlowableEvent flowableEvent) { + if (flowableEvent instanceof FlowableEngineEntityEvent) { + FlowableEngineEntityEvent flowableEngineEvent = (FlowableEngineEntityEvent) flowableEvent; + ProcessDefinitionEntity processDefinition = flowableService.getProcessDefinition(flowableEngineEvent); + if(ObjectUtil.isNull(processDefinition)){ + return; + } + String key = Optional.ofNullable(processDefinition.getKey()).orElse(""); + for (FlowDefEnum flowDefEnum : flowableService.getAllFlowDefEnumsSet()) { + //在流程中存在的才监听 + if (flowDefEnum.getCode().equals(key)) { + TaskEntity entity = (TaskEntity) flowableEngineEvent.getEntity(); + //发送站内信 + List candidateUsers = flowableUserService.getCandidateUsers(entity.getAssignee(), entity.getId()); + if(CollectionUtils.isNotEmpty(candidateUsers)){ + HistoricProcessInstance processInstance = flowableService.getHistoricProcessInstanceById(entity.getProcessInstanceId()); + candidateUsers.forEach(t-> + sendInnerMessage(t,processInstance) + ); + } + } + } + } + + } + + + + private void sendInnerMessage(SysUser toUsers, HistoricProcessInstance processInstance){ + SysUser startSysUser = sysUserServiceImpl.selectUserById(Long.parseLong(processInstance.getStartUserId())); + MessageEventDTO messageEventDTO=new MessageEventDTO(MessageEventType.INNER_TASK_TODO.getCode()); + messageEventDTO.setProducerId(String.valueOf(0)); + messageEventDTO.setConsumerIds(Sets.newHashSet(String.valueOf(toUsers.getUserId()))); + messageEventDTO.setMessageEventType(MessageEventType.INNER_TASK_TODO); + messageEventDTO.setMessageOutsideId(processInstance.getId()); + messageEventDTO.setMessageShow(2); + Map map= Maps.newHashMap(); + map.put("toUser", toUsers.getUserName()); + //map.put("starttime", DateUtil.formatDateTime(hisProcessInstance.getStartTime())); + map.put("startUser", startSysUser.getUserName()); + map.put("processInstance", processInstance.getProcessDefinitionName()); + map.put("url", "http://localhost/flow/getMyHistoricProcessInstance"); + // map.put("datetime", DateUtil.formatDateTime(new Date())); + messageEventDTO.setParamMap(map); + messageEventDTO.setTemplateCode(MessageConstants.INNER_TASK_CREATED_CODE); + applicationContext.publishEvent(messageEventDTO); + } + + + + + @Override + public boolean isFailOnException() { + return false; + } + + @Override + public boolean isFireOnTransactionLifecycleEvent() { + return false; + } + + @Override + public String getOnTransaction() { + return null; + } + +} diff --git a/snow-flowable/src/main/java/com/snow/flowable/service/FlowableService.java b/snow-flowable/src/main/java/com/snow/flowable/service/FlowableService.java index c71160f..701face 100644 --- a/snow-flowable/src/main/java/com/snow/flowable/service/FlowableService.java +++ b/snow-flowable/src/main/java/com/snow/flowable/service/FlowableService.java @@ -1,10 +1,13 @@ package com.snow.flowable.service; import com.snow.common.core.page.PageModel; +import com.snow.flowable.common.enums.FlowDefEnum; import com.snow.flowable.domain.*; import com.snow.system.domain.ActDeModel; import com.snow.system.domain.SysUser; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEvent; import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity; import org.flowable.engine.repository.Model; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; @@ -178,4 +181,18 @@ public interface FlowableService { * @param suspendState */ void suspendOrActiveProcessInstance(String instanceId, Integer suspendState); + /** + * 获取流程定义实体信息 + * + * @param event + * @return ProcessDefinitionEntity + */ + ProcessDefinitionEntity getProcessDefinition(FlowableEngineEvent event); + + + /** + * 获取所有流程定义枚举set + * @return + */ + Set getAllFlowDefEnumsSet(); } diff --git a/snow-flowable/src/main/java/com/snow/flowable/service/FlowableUserService.java b/snow-flowable/src/main/java/com/snow/flowable/service/FlowableUserService.java index e5ef3d8..e67d582 100644 --- a/snow-flowable/src/main/java/com/snow/flowable/service/FlowableUserService.java +++ b/snow-flowable/src/main/java/com/snow/flowable/service/FlowableUserService.java @@ -52,4 +52,13 @@ public interface FlowableUserService { * @return */ Set getFlowGroupByUserId(Long userId); + + + /** + * 获取当前节点的待办人 + * @param assignee 任务分配人 + * @param taskId 任务ID + * @return 待处理人员集合 + */ + List getCandidateUsers(String assignee,String taskId); } diff --git a/snow-flowable/src/main/java/com/snow/flowable/service/impl/FlowableServiceImpl.java b/snow-flowable/src/main/java/com/snow/flowable/service/impl/FlowableServiceImpl.java index 15c81ed..6151e1e 100644 --- a/snow-flowable/src/main/java/com/snow/flowable/service/impl/FlowableServiceImpl.java +++ b/snow-flowable/src/main/java/com/snow/flowable/service/impl/FlowableServiceImpl.java @@ -9,6 +9,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import com.snow.common.core.page.PageModel; import com.snow.common.core.text.Convert; import com.snow.common.enums.WorkRecordStatus; @@ -28,7 +29,9 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.flowable.bpmn.model.*; import org.flowable.bpmn.model.Process; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEvent; import org.flowable.common.engine.impl.identity.Authentication; +import org.flowable.common.engine.impl.interceptor.CommandContext; import org.flowable.common.engine.impl.util.IoUtil; import org.flowable.engine.*; import org.flowable.engine.history.HistoricActivityInstance; @@ -36,6 +39,7 @@ import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.history.HistoricProcessInstanceQuery; import org.flowable.engine.impl.RepositoryServiceImpl; import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity; +import org.flowable.engine.impl.util.CommandContextUtil; import org.flowable.engine.repository.*; import org.flowable.engine.runtime.Execution; import org.flowable.engine.runtime.ProcessInstance; @@ -1164,4 +1168,28 @@ public class FlowableServiceImpl implements FlowableService { return sysUserService.selectUserById(Long.parseLong(id)).getUserName(); } + + /** + * 获取流程定义实体信息 + * + * @param event + * @return ProcessDefinitionEntity + */ + public ProcessDefinitionEntity getProcessDefinition(FlowableEngineEvent event) { + String processDefinitionId = event.getProcessDefinitionId(); + if (processDefinitionId != null) { + CommandContext commandContext = CommandContextUtil.getCommandContext(); + if (commandContext != null) { + return CommandContextUtil.getProcessDefinitionEntityManager(commandContext).findById(processDefinitionId); + } + } + return null; + } + + @Override + public Set getAllFlowDefEnumsSet() { + FlowDefEnum[] values = FlowDefEnum.values(); + return Sets.newHashSet(values); + } + } diff --git a/snow-flowable/src/main/java/com/snow/flowable/service/impl/FlowableUserServiceImpl.java b/snow-flowable/src/main/java/com/snow/flowable/service/impl/FlowableUserServiceImpl.java index a3305bf..99e1736 100644 --- a/snow-flowable/src/main/java/com/snow/flowable/service/impl/FlowableUserServiceImpl.java +++ b/snow-flowable/src/main/java/com/snow/flowable/service/impl/FlowableUserServiceImpl.java @@ -1,5 +1,6 @@ package com.snow.flowable.service.impl; +import cn.hutool.core.util.ObjectUtil; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.snow.common.constant.UserConstants; @@ -12,6 +13,8 @@ import com.snow.system.service.IFlowGroupDOService; import com.snow.system.service.ISysRoleService; import com.snow.system.service.ISysUserService; import lombok.extern.slf4j.Slf4j; +import org.flowable.identitylink.api.IdentityLink; +import org.flowable.engine.TaskService; import org.flowable.ui.common.model.RemoteGroup; import org.flowable.ui.common.model.RemoteUser; import org.springframework.beans.factory.annotation.Autowired; @@ -40,6 +43,9 @@ public class FlowableUserServiceImpl implements FlowableUserService { @Autowired private IFlowGroupDOService flowGroupDOService; + @Autowired + private TaskService taskService; + @Override public Map loginFlowable() { Map map = new HashMap<>(); @@ -199,4 +205,36 @@ public class FlowableUserServiceImpl implements FlowableUserService { return parentFlowGroup; } + + /** + * 获取当前节点的待办人 + * @param assignee 任务分配人 + * @param taskId 任务ID + * @return 待处理人员集合 + */ + public List getCandidateUsers(String assignee,String taskId) { + Set result = Sets.newHashSet(); + if (ObjectUtil.isNotNull(assignee)) { + // 已经被指派了,则可审批人就是指派的人 + SysUser sysUser = iSysUserService.selectUserById(Long.parseLong(assignee)); + if (sysUser != null) { + result.add(sysUser); + } + } else { + // 获取待办对应的groupId和userId + List identityLinks = taskService.getIdentityLinksForTask(taskId); + for (IdentityLink identityLink : identityLinks) { + if (ObjectUtil.isNotNull(identityLink.getGroupId())) { + List sysUsers = getUserByFlowGroupId(Long.parseLong(identityLink.getGroupId())); + result.addAll(sysUsers); + } else if (ObjectUtil.isNotNull(identityLink.getUserId())) { + SysUser handleUser = iSysUserService.selectUserById(Long.parseLong(identityLink.getUserId())); + if (handleUser != null) { + result.add(handleUser); + } + } + } + } + return new ArrayList<>(result); + } } diff --git a/snow-framework/src/main/java/com/snow/framework/web/message/MessageEventHandler.java b/snow-framework/src/main/java/com/snow/framework/web/message/MessageEventHandler.java index dcf92b1..8efe3d6 100644 --- a/snow-framework/src/main/java/com/snow/framework/web/message/MessageEventHandler.java +++ b/snow-framework/src/main/java/com/snow/framework/web/message/MessageEventHandler.java @@ -38,6 +38,7 @@ public class MessageEventHandler implements ApplicationListener break; case "SEND_VISIT_LOG": case "REGISTER_ACCOUNT_SUCCESS": + case "INNER_TASK_TODO": messageEventContext.setMessageEventTypeStrategy(new InnerMessageStrategy()); break; diff --git a/sql/v1.8.sql b/sql/v1.8.sql index 6c148d3..9cf65d8 100644 --- a/sql/v1.8.sql +++ b/sql/v1.8.sql @@ -13,3 +13,7 @@ INSERT INTO `snow`.`sys_dict_type`( `dict_name`, `dict_type`, `status`, `create_ INSERT INTO `snow`.`sys_dict_data`( `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1, '系统用户', '00', 'sys_user_type', 'info', '', 'Y', '0', 'admin', NOW(), 'ry', NOW(), '系统用户'); INSERT INTO `snow`.`sys_dict_data`( `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2, '官网用户', '01', 'sys_user_type', 'primary', '', 'N', '0', 'admin', NOW(), 'ry', NOW(), '官网用户'); + +#2021-07-16 + +INSERT INTO `snow`.`sys_message_template`( `template_code`, `template_name`, `template_body`, `template_desc`, `template_type`, `pc_url`, `app_url`, `icon_class`, `template_status`, `is_delete`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ( '1415927384573616128', '任务待办', '尊敬的${toUser}:\r\n 您好,由${startUser}发起的${processInstance}。需要您去协助处理。\r\n 前往处理${url}。', '任务待办', 4, '', '', 'fa fa-bullhorn', 0, 0, '1', NOW(), NULL, NULL); \ No newline at end of file