From f2158491a9638e95c46f57257749a1854452ae17 Mon Sep 17 00:00:00 2001
From: Echo <4759156@qq.com>
Date: Tue, 27 Feb 2024 10:49:12 +0800
Subject: [PATCH] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E5=B0=8F=E7=A8=8B=E5=BA=8F?=
=?UTF-8?q?=E8=AE=A2=E9=98=85=E6=B6=88=E6=81=AF=E6=A8=A1=E7=89=88?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
yudao-module-bpm/yudao-module-bpm-biz/pom.xml | 5 --
.../convert/message/BpmMessageConvert.java | 30 ++++-----
.../task/BpmProcessInstanceConvert.java | 8 ++-
.../bpm/convert/task/BpmTaskConvert.java | 31 +++++++++-
.../rpc/config/RpcConfiguration.java | 2 +-
.../message/BpmMessageServiceImpl.java | 61 +++++++++----------
...eSendWhenProcessInstanceApproveReqDTO.java | 10 +++
...geSendWhenProcessInstanceRejectReqDTO.java | 5 ++
.../BpmMessageSendWhenTaskCreatedReqDTO.java | 10 +++
.../task/BpmProcessInstanceServiceImpl.java | 2 +-
.../bpm/service/task/BpmTaskServiceImpl.java | 7 ++-
.../subscribe/SubscribeMessageSendApi.java | 4 +-
.../subscribe/dto/SubscribeMessageReqDTO.java | 9 +--
.../SubscribeMessageSendApiImpl.java | 28 +++++++--
.../service/auth/AdminAuthServiceImpl.java | 2 +-
.../service/social/SocialClientService.java | 7 +++
.../social/SocialClientServiceImpl.java | 5 ++
.../src/main/resources/application-local.yaml | 4 +-
.../src/main/resources/application-prod.yaml | 4 +-
19 files changed, 158 insertions(+), 76 deletions(-)
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/pom.xml b/yudao-module-bpm/yudao-module-bpm-biz/pom.xml
index 7578e4ef..9b4125d2 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/pom.xml
+++ b/yudao-module-bpm/yudao-module-bpm-biz/pom.xml
@@ -115,11 +115,6 @@
yudao-spring-boot-starter-flowable
-
- com.github.binarywang
- wx-java-miniapp-spring-boot-starter
-
-
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/message/BpmMessageConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/message/BpmMessageConvert.java
index e18db91e..3b513622 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/message/BpmMessageConvert.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/message/BpmMessageConvert.java
@@ -1,10 +1,10 @@
package cn.iocoder.yudao.module.bpm.convert.message;
-import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
-import cn.binarywang.wx.miniapp.constant.WxMaConstants;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import cn.iocoder.yudao.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO;
+import cn.iocoder.yudao.module.system.api.subscribe.dto.MsgData;
+import cn.iocoder.yudao.module.system.api.subscribe.dto.SubscribeMessageReqDTO;
import org.flowable.bpmn.model.UserTask;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@@ -40,36 +40,36 @@ public interface BpmMessageConvert {
* @param miniProgramState 小程序的状态
* @return
*/
- default WxMaSubscribeMessage convertApprovalResultNotification(String openId, String processInstanceName, String time, String result, String reason, String miniProgramState) {
- WxMaSubscribeMessage message = new WxMaSubscribeMessage() ;
+ default SubscribeMessageReqDTO convertApprovalResultNotification(String openId, String processInstanceName, String time, String result, String reason, String miniProgramState) {
+ SubscribeMessageReqDTO message = new SubscribeMessageReqDTO() ;
message.setToUser(openId) ;
message.setTemplateId("OnJjp5pdjG1PHMoELYaqp3Xq8jWZ5E6ndO0clEIQ4tk") ;
//审批类型
- WxMaSubscribeMessage.MsgData processType = new WxMaSubscribeMessage.MsgData();
+ MsgData processType = new MsgData();
processType.setName("thing1") ;
processType.setValue(processInstanceName) ;
message.addData(processType);
//发起时间
- WxMaSubscribeMessage.MsgData createTime = new WxMaSubscribeMessage.MsgData();
+ MsgData createTime = new MsgData();
createTime.setName("time2") ;
createTime.setValue(time) ;
message.addData(createTime);
//审批时间
- WxMaSubscribeMessage.MsgData approvalTime = new WxMaSubscribeMessage.MsgData();
+ MsgData approvalTime = new MsgData();
approvalTime.setName("time7") ;
approvalTime.setValue(DateUtils.dateFormat(new Date(),DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)) ;
message.addData(approvalTime);
//审批结果
- WxMaSubscribeMessage.MsgData approvalResult = new WxMaSubscribeMessage.MsgData();
+ MsgData approvalResult = new MsgData();
approvalResult.setName("phrase3") ;
approvalResult.setValue(result) ;
message.addData(approvalResult);
if(reason != null ) {
- WxMaSubscribeMessage.MsgData approvalReason = new WxMaSubscribeMessage.MsgData();
+ MsgData approvalReason = new MsgData();
approvalReason.setName("thing12") ;
approvalReason.setValue(reason) ;
message.addData(approvalReason);
@@ -89,30 +89,30 @@ public interface BpmMessageConvert {
* @param miniProgramState 小程序的状态
* @return
*/
- default WxMaSubscribeMessage convertProcessToDoReminder(String openId, String processInstanceName,String startUserNickname, String time, String schedule, String miniProgramState) {
- WxMaSubscribeMessage message = new WxMaSubscribeMessage() ;
+ default SubscribeMessageReqDTO convertProcessToDoReminder(String openId, String processInstanceName,String startUserNickname, String time, String schedule, String miniProgramState) {
+ SubscribeMessageReqDTO message = new SubscribeMessageReqDTO() ;
message.setToUser(openId) ;
message.setTemplateId("3cP4btlFSSiZk65qVewN_WoT_bh0OfUkYzzTsADOrR4") ;
//待办标题
- WxMaSubscribeMessage.MsgData processType = new WxMaSubscribeMessage.MsgData();
+ MsgData processType = new MsgData();
processType.setName("thing1") ;
processType.setValue("您收到了一条新的待办任务:"+processInstanceName) ;
message.addData(processType);
//申请人
- WxMaSubscribeMessage.MsgData applicant = new WxMaSubscribeMessage.MsgData();
+ MsgData applicant = new MsgData();
applicant.setName("thing4") ;
applicant.setValue(startUserNickname) ;
message.addData(applicant);
//申请时间
- WxMaSubscribeMessage.MsgData createTime = new WxMaSubscribeMessage.MsgData();
+ MsgData createTime = new MsgData();
createTime.setName("time5") ;
createTime.setValue(time) ;
message.addData(createTime);
//当前进度
- WxMaSubscribeMessage.MsgData currentSchedule = new WxMaSubscribeMessage.MsgData();
+ MsgData currentSchedule = new MsgData();
currentSchedule.setName("thing6") ;
currentSchedule.setValue(schedule) ;
message.addData(currentSchedule);
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
index 08239734..bc1ee019 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
@@ -140,8 +140,12 @@ public interface BpmProcessInstanceConvert {
return event;
}
- default BpmMessageSendWhenProcessInstanceApproveReqDTO convert2ApprovedReq(ProcessInstance instance){
+ default BpmMessageSendWhenProcessInstanceApproveReqDTO convert2ApprovedReq(ProcessInstance instance, String reason){
return new BpmMessageSendWhenProcessInstanceApproveReqDTO()
+ //添加原因
+ .setReason(reason)
+ //添加流程实际创建时间
+ .setCreateTime(DateUtils.dateFormat(instance.getStartTime(),DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) )
.setStartUserId(NumberUtils.parseLong(instance.getStartUserId()))
.setProcessInstanceId(instance.getId())
.setProcessInstanceName(instance.getName());
@@ -149,6 +153,8 @@ public interface BpmProcessInstanceConvert {
default BpmMessageSendWhenProcessInstanceRejectReqDTO convert2RejectReq(ProcessInstance instance, String reason) {
return new BpmMessageSendWhenProcessInstanceRejectReqDTO()
+ //添加流程实际创建时间
+ .setCreateTime(DateUtils.dateFormat(instance.getStartTime(),DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) )
.setProcessInstanceName(instance.getName())
.setProcessInstanceId(instance.getId())
.setReason(reason)
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
index d3398678..6f25d725 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
@@ -10,6 +10,7 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmConstants;
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
@@ -157,9 +158,37 @@ public interface BpmTaskConvert {
}
default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, AdminUserRespDTO startUser,
- Task task) {
+ Task task, List BpmTaskRespVOs) {
BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO();
+
+ //获取流程审批列表最大数-2的记录数据
+ /**
+ * 因为必须存在一条记录,因为流程设计的时候,第一个审批节点是自己。并且自动审核通过的。
+ * 所以到真实审批人了,就会有2条审批记录
+ * 如果是真实审批人,获取审批进度的话,就需要获取前一条记录的数据。
+ * 因为数据下标是从0开始的, 所以需要减2
+ * List BpmTaskRespVOs的数据顺序是:
+ * 发起人是在最后显示的
+ */
+ String schedule = "" ;
+ if(BpmTaskRespVOs.size() >= 2 ) {
+ //List中的第0条记录,即是当前需要审核人
+ //List中最后一条记录,即是发起人的自动审核记录
+ BpmTaskRespVO bpmTaskRespVO = BpmTaskRespVOs.get( 1 ) ;
+ if( bpmTaskRespVO.getReason() !=null && bpmTaskRespVO.getReason().equals(BpmConstants.AUTO_APPRAVAL) ) {
+ //bpmTaskRespVO.getAssigneeUser().getDeptName() + ":"+
+ schedule = bpmTaskRespVO.getAssigneeUser().getNickname()+"发起审批!" ;
+ }else {
+ schedule = bpmTaskRespVO.getAssigneeUser().getNickname()+"已审批通过";
+ }
+ }else {
+ schedule = startUser.getNickname()+"发起审批" ;
+ }
reqDTO.setProcessInstanceId(processInstance.getProcessInstanceId())
+ //添加流程进度
+ .setSchedule(schedule)
+ //添加流程实际创建时间
+ .setCreateTime(DateUtils.dateFormat(processInstance.getStartTime(),DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) )
.setProcessInstanceName(processInstance.getName()).setStartUserId(startUser.getId())
.setStartUserNickname(startUser.getNickname()).setTaskId(task.getId()).setTaskName(task.getName())
.setAssigneeUserId(NumberUtils.parseLong(task.getAssignee()));
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/rpc/config/RpcConfiguration.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/rpc/config/RpcConfiguration.java
index 1166db35..00b8fd21 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/rpc/config/RpcConfiguration.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/rpc/config/RpcConfiguration.java
@@ -14,7 +14,7 @@ import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = {FileApi.class,RoleApi.class, DeptApi.class, PostApi.class, AdminUserApi.class, SmsSendApi.class, DictDataApi.class, NotifyMessageSendApi.class,
-// SubscribeMessageSendApi.class
+ SubscribeMessageSendApi.class
})
public class RpcConfiguration {
}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageServiceImpl.java
index a5680750..239e4f9a 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageServiceImpl.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageServiceImpl.java
@@ -1,9 +1,5 @@
package cn.iocoder.yudao.module.bpm.service.message;
-import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
-import cn.binarywang.wx.miniapp.constant.WxMaConstants;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.web.config.WebProperties;
import cn.iocoder.yudao.module.bpm.convert.message.BpmMessageConvert;
@@ -12,7 +8,6 @@ import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcess
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
-import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import cn.iocoder.yudao.module.system.api.sms.SmsSendApi;
import cn.iocoder.yudao.module.system.api.subscribe.SubscribeMessageSendApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -60,13 +55,13 @@ public class BpmMessageServiceImpl implements BpmMessageService {
//发送审批结果通知
String openId = getUserOpenId(reqDTO.getStartUserId()) ;
if(openId != null ) {
-// subscribeMessageSendApi.sendApprovalResultNotification(
-// BpmMessageConvert.INSTANCE.convertApprovalResultNotification(
-// openId,reqDTO.getProcessInstanceName(), "","通过", null,
-// /**
-// * 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
-// */
-// WxMaConstants.MiniProgramState.DEVELOPER) ) ;
+ subscribeMessageSendApi.sendApprovalResultNotification(
+ BpmMessageConvert.INSTANCE.convertApprovalResultNotification(
+ openId,reqDTO.getProcessInstanceName(), reqDTO.getCreateTime(), "通过", reqDTO.getReason(),
+ /**
+ * 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
+ */
+ "formal") ) ;
}
}
@@ -82,16 +77,16 @@ public class BpmMessageServiceImpl implements BpmMessageService {
notifyMessageSendApi.sendSingleMessageToAdmin(BpmMessageConvert.INSTANCE.convert1(
reqDTO.getStartUserId(), BpmMessageEnum.PROCESS_INSTANCE_REJECT.getSmsTemplateCode(), templateParams));
- //发送审批结果通知
+// //发送审批结果通知
String openId = getUserOpenId(reqDTO.getStartUserId()) ;
if(openId != null ) {
-// subscribeMessageSendApi.sendApprovalResultNotification(
-// BpmMessageConvert.INSTANCE.convertApprovalResultNotification(
-// openId,reqDTO.getProcessInstanceName(),"","不通过", reqDTO.getReason(),
-// /**
-// * 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
-// */
-// WxMaConstants.MiniProgramState.DEVELOPER) ) ;
+ subscribeMessageSendApi.sendApprovalResultNotification(
+ BpmMessageConvert.INSTANCE.convertApprovalResultNotification(
+ openId,reqDTO.getProcessInstanceName(),reqDTO.getCreateTime(),"不通过", reqDTO.getReason(),
+ /**
+ * 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
+ */
+ "formal") ) ;
}
}
@@ -99,30 +94,30 @@ public class BpmMessageServiceImpl implements BpmMessageService {
@Override
public void sendMessageWhenTaskAssigned(BpmMessageSendWhenTaskCreatedReqDTO reqDTO) {
//任务分配
- Long loginUserId = SecurityFrameworkUtils.getLoginUserId() ;
- Long startUserId = reqDTO.getStartUserId();
- //如果发起人和接受人是同一个人,那么不发送站内信,否则就发送
- if(!loginUserId.toString().equals(startUserId.toString())) {
+ Long startUserId = reqDTO.getStartUserId(); //流程发起人
+ Long assigneeUserId = reqDTO.getAssigneeUserId() ; //任务审批人
+ String taskName = reqDTO.getTaskName() ;
+ //如果流程发起人和任务审批是同一个人, 那么不发送站内信,否则就发送
+ if(!assigneeUserId.toString().equals(startUserId.toString())) {
Map templateParams = new HashMap<>();
templateParams.put("processInstanceName", reqDTO.getProcessInstanceName());
-// templateParams.put("taskName", reqDTO.getTaskName());
+// templateParams.put("taskName", reqDTO.getTaskName());
templateParams.put("startUserNickname", reqDTO.getStartUserNickname());
templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId()));
+ //短信
// smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getAssigneeUserId(),
// BpmMessageEnum.TASK_ASSIGNED.getSmsTemplateCode(), templateParams));
+ //站内信
notifyMessageSendApi.sendSingleMessageToAdmin(BpmMessageConvert.INSTANCE.convert1(
reqDTO.getAssigneeUserId(), BpmMessageEnum.TASK_ASSIGNED.getSmsTemplateCode(), templateParams));
-
+ //微信小程序订阅消息
//发送OA流程待办提醒
- String openId = getUserOpenId(reqDTO.getStartUserId()) ;
+ String openId = getUserOpenId(reqDTO.getStartUserId()) ; //只有在微信小程序登陆过用户才会有openid
if(openId != null ) {
-
-// subscribeMessageSendApi.sendProcessToDoReminder( BpmMessageConvert.INSTANCE.convertProcessToDoReminder(
-// openId, reqDTO.getProcessInstanceName(), reqDTO.getStartUserNickname(),"","",
-// WxMaConstants.MiniProgramState.DEVELOPER ) ) ;
-
+ subscribeMessageSendApi.sendProcessToDoReminder( BpmMessageConvert.INSTANCE.convertProcessToDoReminder(
+ openId, reqDTO.getProcessInstanceName(), reqDTO.getStartUserNickname(),reqDTO.getCreateTime(),reqDTO.getSchedule() ,
+ "formal" ) ) ;
}
-
}
}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java
index 7c37734f..8de6c4fe 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java
@@ -24,4 +24,14 @@ public class BpmMessageSendWhenProcessInstanceApproveReqDTO {
@NotNull(message = "发起人的用户编号")
private Long startUserId;
+ /**
+ * 流程实例创建的时间
+ */
+ @NotNull(message = "流程实例创建的时间")
+ private String createTime ;
+ /**
+ * 通过理由
+ */
+ @NotEmpty(message = "通过理由不能为空")
+ private String reason;
}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java
index 69a266b6..6a9e198f 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java
@@ -24,6 +24,11 @@ public class BpmMessageSendWhenProcessInstanceRejectReqDTO {
@NotNull(message = "发起人的用户编号")
private Long startUserId;
+ /**
+ * 流程实例创建的时间
+ */
+ @NotNull(message = "流程实例创建的时间")
+ private String createTime ;
/**
* 不通过理由
*/
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java
index 7cb0e3d7..70b09df8 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java
@@ -43,4 +43,14 @@ public class BpmMessageSendWhenTaskCreatedReqDTO {
@NotNull(message = "审批人的用户编号不能为空")
private Long assigneeUserId;
+ /**
+ * 流程实例创建的时间
+ */
+ private String createTime ;
+
+ /**
+ * 流程进度
+ */
+ private String schedule ;
+
}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
index 280f6f04..e948c1aa 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
@@ -1 +1 @@
-package cn.iocoder.yudao.module.bpm.service.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO;
import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmConstants;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
/**
* 流程实例 Service 实现类
*
* ProcessDefinition & ProcessInstance & Execution & Task 的关系:
* 1.
*
* HistoricProcessInstance & ProcessInstance 的关系:
* 1.
*
* 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例
*
*/
@Service
@Validated
@Slf4j
public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService {
@Resource
private TaskService engineTaskService;
@Resource
private BpmTaskAssignRuleMapper taskRuleMapper;
@Resource
private BpmUserGroupService userGroupService;
@Resource
private RuntimeService runtimeService;
@Resource
private BpmProcessInstanceExtMapper processInstanceExtMapper;
@Resource
@Lazy // 解决循环依赖
private BpmTaskService taskService;
@Resource
private BpmProcessDefinitionService processDefinitionService;
@Resource
private HistoryService historyService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private DeptApi deptApi;
@Resource
private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher;
@Resource
private BpmMessageService messageService;
@Override
public ProcessInstance getProcessInstance(String id) {
return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult();
}
@Override
public List getProcessInstances(Set ids) {
return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
}
@Override
public PageResult getMyProcessInstancePage(Long userId,
BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
List ids = taskMap.values().stream()
.flatMap(Collection::stream)
.map(Task::getAssignee)
.collect(Collectors.toList());
List longIds = ids.stream()
.map(Long::valueOf)
.collect(Collectors.toList());
// 获得 User Map
Map userMap = adminUserApi.getUserMap(longIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap, userMap);
}
@Override
@Transactional(rollbackFor = Exception.class)
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId());
// 发起流程
return createProcessInstance0(userId, definition, createReqVO.getVariables(), null);
}
@Override
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
// 发起流程
return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey());
}
@Override
public BpmProcessInstanceRespVO getProcessInstanceVO(String id) {
// 获得流程实例
HistoricProcessInstance processInstance = getHistoricProcessInstance(id);
if (processInstance == null) {
return null;
}
BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id);
Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id);
// 获得流程定义
ProcessDefinition processDefinition = processDefinitionService
.getProcessDefinition(processInstance.getProcessDefinitionId());
Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId());
BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt(
processInstance.getProcessDefinitionId());
Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id);
String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId());
// 获得 User
AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData();
DeptRespDTO dept = null;
if (startUser != null) {
dept = deptApi.getDept(startUser.getDeptId()).getCheckedData();
}
// 拼接结果
return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt,
processDefinition, processDefinitionExt, bpmnXml, startUser, dept);
}
@Override
public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) {
// 校验流程实例存在
ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
if (instance == null) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
}
// 只能取消自己的
if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
}
// 通过删除流程实例,实现流程实例的取消,
// 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询
deleteProcessInstance(cancelReqVO.getId(),
BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason()));
}
/**
* 获得历史的流程实例
*
* @param id 流程实例的编号
* @return 历史的流程实例
*/
@Override
public HistoricProcessInstance getHistoricProcessInstance(String id) {
return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult();
}
@Override
public List getHistoricProcessInstances(Set ids) {
return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list();
}
@Override
public void createProcessInstanceExt(ProcessInstance instance) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId());
// 插入 BpmProcessInstanceExtDO 对象
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(instance.getId())
.setProcessDefinitionId(definition.getId())
.setName(instance.getProcessDefinitionName())
.setStartUserId(Long.valueOf(instance.getStartUserId()))
.setCategory(definition.getCategory())
.setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus())
.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
processInstanceExtMapper.insert(instanceExtDO);
}
@Override
public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) {
// 判断是否为 Reject 不通过。如果是,则不进行更新.
// 因为,updateProcessInstanceExtReject 方法,已经进行更新了
if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String)event.getCause())) {
return;
}
// 需要主动查询,因为 instance 只有 id 属性
// 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId());
// 更新拓展表
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(event.getProcessInstanceId())
.setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.CANCEL.getResult());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
@Override
public void updateProcessInstanceExtComplete(ProcessInstance instance) {
// 需要主动查询,因为 instance 只有 id 属性
// 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId());
// 更新拓展表
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(instance.getProcessInstanceId())
.setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
// 发送流程被通过的消息
messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance));
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateProcessInstanceExtReject(String id, String reason) {
// 需要主动查询,因为 instance 只有 id 属性
ProcessInstance processInstance = getProcessInstance(id);
// 删除流程实例,以实现驳回任务时,取消整个审批流程
deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason)));
// 更新 status + result
// 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法,
// 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id)
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.REJECT.getResult());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
// 发送流程被不通过的消息
messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason));
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
private void deleteProcessInstance(String id, String reason) {
runtimeService.deleteProcessInstance(id, reason);
}
private String createProcessInstance0(Long userId, ProcessDefinition definition,
Map variables, String businessKey) {
// 校验流程定义
if (definition == null) {
throw exception(PROCESS_DEFINITION_NOT_EXISTS);
}
if (definition.isSuspended()) {
throw exception(PROCESS_DEFINITION_IS_SUSPENDED);
}
// 创建流程实例
ProcessInstance instance = runtimeService.createProcessInstanceBuilder()
.processDefinitionId(definition.getId())
.businessKey(businessKey)
.name(definition.getName().trim())
.variables(variables)
.start();
// 设置流程名字
runtimeService.setProcessInstanceName(instance.getId(), definition.getName());
// 补全流程实例的拓展表
processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId())
.setFormVariables(variables));
/** 创建流程后,添加抄送人 End add by yj 2024.1.4 */
processCCToUsers(definition,instance) ;
/** 通过自己发起的流程 */
approveSelfTask(instance.getId()) ;
return instance.getId();
}
private void approveSelfTask(String processInstanceId ) {
List tasks =engineTaskService.createTaskQuery().processInstanceId(processInstanceId).list() ;
if( tasks != null && tasks.size() > 0) {
Task task = tasks.get(0) ;
String assigneeId = task.getAssignee();
//如果当前登陆用户是审批人,那么自动审批通过
if( assigneeId.equals( SecurityFrameworkUtils.getLoginUserId().toString() )) {
BpmTaskApproveReqVO reqVO = new BpmTaskApproveReqVO() ;
reqVO.setId(task.getId()) ;
reqVO.setReason(BpmConstants.AUTO_APPRAVAL);
taskService.approveTask(getLoginUserId(), reqVO);
}
}
}
/**
* 获得抄送我的流程实例的分页
*
* @param userId 用户编号
* @param pageReqVO 分页请求
* @return 流程实例的分页
*/
public PageResult getMyCCProcessInstancePage(Long userId,
BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectCCPage(userId, pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap,null);
}
public List getProcessInstancesGroupByModelName(BpmProcessInstanceStatisticsReqVO pageReqVO){
pageReqVO = getUserids(pageReqVO) ;
return processInstanceExtMapper.getProcessInstancesGroupByModelName(pageReqVO) ;
}
public List getProcessInstancesGroupByResultStatus(BpmProcessInstanceStatisticsReqVO pageReqVO){
pageReqVO = getUserids(pageReqVO) ;
return processInstanceExtMapper.getProcessInstancesGroupByResultStatus(pageReqVO) ;
}
public PageResult getStatisticsProcessInstancePage(@Valid BpmProcessInstanceMyPageReqVO pageReqVO) {
pageReqVO = getUserids(pageReqVO) ;
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectStatisticePage(pageReqVO.getUserIds(), pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
List ids = taskMap.values().stream()
.flatMap(Collection::stream)
.map(Task::getAssignee)
.collect(Collectors.toList());
List longIds = ids.stream()
.map(Long::valueOf)
.collect(Collectors.toList());
//获得 审批人 User Map
Map assigneeUserMap = adminUserApi.getUserMap(longIds);
//获得 发起人 User Map
List bpieDOs = pageResult.getList() ;
longIds = new ArrayList<>() ;
for (BpmProcessInstanceExtDO bpieDO: bpieDOs) {
longIds.add(bpieDO.getStartUserId()) ;
}
Map startUserMap = adminUserApi.getUserMap(longIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertStatisticsPage(pageResult, taskMap, assigneeUserMap,startUserMap);
}
@Resource
PermissionApi permissionApi;
/**
* 根据数据权限,查询关联操作的用户IDS
* @param pageReqVO
* @return
* @param
*/
private T getUserids(T pageReqVO) {
try {
Class clazz = (Class) pageReqVO.getClass();
Field idField = clazz.getDeclaredField("userIds");
idField.setAccessible(true); // 设置可访问性
Long[] userIds = null;
Long userId = WebFrameworkUtils.getLoginUserId();
DeptDataPermissionRespDTO deptDataPermission = permissionApi.getDeptDataPermission(userId).getCheckedData();
//查询全部
if (deptDataPermission.getAll()) {
//idField.set(pageReqVO, null); // 设置属性值
return pageReqVO;
}
// 情况二,即不能查看部门,又不能查看自己,则说明 100% 无权限
if (CollUtil.isEmpty(deptDataPermission.getDeptIds())
&& Boolean.FALSE.equals(deptDataPermission.getSelf())) {
//设置成0,一个不存在的用户Id,就查询不到数据了。
userIds = new Long[]{0L};
idField.set(pageReqVO, userIds);
return pageReqVO;
}
//情况三 至查询自己
if (deptDataPermission.getSelf()) {
userIds = new Long[]{userId};
idField.set(pageReqVO, userIds);
return pageReqVO;
}
Set deptIds = deptDataPermission.getDeptIds();
//查询部门关联的用户Id
List users = adminUserApi.getUserListByDeptIds(deptIds).getCheckedData();
List tempList = new ArrayList<>();
for (AdminUserRespDTO user : users) {
Long id = user.getId();
tempList.add(id);
}
tempList.add(userId);
userIds = tempList.stream().toArray(Long[]::new); //将临时的List集合转换成数组集合
idField.set(pageReqVO, userIds);
return pageReqVO;
}catch (Exception exception){
exception.printStackTrace();
throw exception(BPM_SYSTEM_BUG);
}
}
/**
* /创建流程后,添加抄送人 Begin add by yj 2024.1.4
* 在设计流程的时候,需要添加一个任务块 名字必须叫Activity_cc 分配权限的时候,需要选择用户组。
*
* @param definition
* @param instance
*/
private void processCCToUsers(ProcessDefinition definition, ProcessInstance instance) {
//获取bpm_task_assign_reule (Bpm 任务规则表)的流程中有没有配置抄送节点 固定抄送名称为:Activity_cc
String processDefinitionId = definition.getId() ;
List rules = taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, null);
for(BpmTaskAssignRuleDO rule :rules ){
String key = rule.getTaskDefinitionKey() ; //任务名称
Integer type = rule.getType() ;
if( !key.isEmpty() && key.equals(BpmConstants.CC_NAME) && type == 40 ) {
StringBuffer str = new StringBuffer() ;
Set options = rule.getOptions() ;
List list = new ArrayList(options);
for(Long groupId : list) {
//需要根据这个groupId,查询这个组中的用户id
BpmUserGroupDO userGroup = userGroupService.getUserGroup(groupId);
Set userIds = userGroup.getMemberUserIds() ;
List userIdList = new ArrayList(userIds);
for(Long user_id : userIdList) {
str.append("[").append(user_id).append("]") ;
}
}
String ccids = str.toString() ;
//根据processDefinitionId 将ccids保存到bpm_process_instance_ext中的ccids字段
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessDefinitionId(processDefinitionId)
.setCcids(ccids) .setProcessInstanceId(instance.getProcessInstanceId());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
break ;
}
}
}
@Resource
private BpmTaskExtMapper taskExtMapper;
@Override
public List getUserProcessTpo10(BpmProcessInstanceStatisticsReqVO pageReqVO) {
//按审核人分组查询,统计已完成流程数量及平均耗时间
List respVOS = processInstanceExtMapper.getUserProcessTpo10(pageReqVO);
if( respVOS == null || respVOS.size() == 0) {
return null ;
}
List idList = respVOS.stream()
.map(BpmProcessFinishStatisticsRespVO::getUserId)
.collect(Collectors.toList());
//根据userId,查询UserMap
Map userMap = adminUserApi.getUserMap(idList) ;
Long[] idsArray = new Long[userMap.size()];
// 使用 map 的 keySet() 方法获取键集合
Set keys = userMap.keySet();
// 遍历键集合,将每个键添加到数组中
int index = 0;
for (Long key : keys) {
idsArray[index++] = key;
}
pageReqVO.setUserIds(idsArray) ;
//按审核人分组查询,未审批完成的流程数量
List bpmTaskExtDOs = processInstanceExtMapper.selectUnfinishProcessCount( pageReqVO ) ;
//按审核人分组查询,未完成的记录数
Map unFinfishCountCountMap = new HashMap<>();
for (BpmProcessFinishStatisticsRespVO item : bpmTaskExtDOs) {
unFinfishCountCountMap.put(item.getUserId(), item.getUnFinfishCount());
}
respVOS.forEach(respVO -> respVO.setName( userMap.get(respVO.getUserId()).getNickname()));
respVOS.forEach(respVO -> respVO.setUnFinfishCount( unFinfishCountCountMap.get(respVO.getUserId())));
//获取排行榜第一记录的耗时,作为基础数据
double num = Double.parseDouble(respVOS.get(0).getUserTime()); // 先将字符串转换为双精度浮点数
int baseNumber = (int)num; // 再通过类型转换操作符将其转换为整数
DecimalFormat df = new DecimalFormat("#.00");
respVOS.forEach(respVO -> {
//格式化流程完成率
float finfishCount = (float) respVO.getFinfishCount() ;
float unFinfishCount = respVO.getUnFinfishCount() == null ? 0: respVO.getUnFinfishCount();
float all = finfishCount + unFinfishCount ;
float result = finfishCount / all;
float rate = result * 100 ;
respVO.setCompletionRate(df.format(rate)+"%") ;
double dValue = Double.parseDouble(respVO.getUserTime()); // 将字符串转换为double类型
int roundedValue = (int) Math.round(dValue); // 使用Math.round()进行四舍五入,并转换为int类型
respVO.setUserTime(roundedValue+"") ;
//格式化进度百度比, 参照最高的数据进行百分比显示
double percentage = ((int)Double.parseDouble(respVO.getUserTime()) / (double) baseNumber) * 100;
respVO.setPercentage((int) Math.round(percentage)) ;
//设置未完成
respVO.setUnFinfishCount( Integer.valueOf((int)unFinfishCount)) ;
});
return respVOS ;
}
}
\ No newline at end of file
+package cn.iocoder.yudao.module.bpm.service.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO;
import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmConstants;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
/**
* 流程实例 Service 实现类
*
* ProcessDefinition & ProcessInstance & Execution & Task 的关系:
* 1.
*
* HistoricProcessInstance & ProcessInstance 的关系:
* 1.
*
* 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例
*
*/
@Service
@Validated
@Slf4j
public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService {
@Resource
private TaskService engineTaskService;
@Resource
private BpmTaskAssignRuleMapper taskRuleMapper;
@Resource
private BpmUserGroupService userGroupService;
@Resource
private RuntimeService runtimeService;
@Resource
private BpmProcessInstanceExtMapper processInstanceExtMapper;
@Resource
@Lazy // 解决循环依赖
private BpmTaskService taskService;
@Resource
private BpmProcessDefinitionService processDefinitionService;
@Resource
private HistoryService historyService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private DeptApi deptApi;
@Resource
private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher;
@Resource
private BpmMessageService messageService;
@Override
public ProcessInstance getProcessInstance(String id) {
return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult();
}
@Override
public List getProcessInstances(Set ids) {
return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
}
@Override
public PageResult getMyProcessInstancePage(Long userId,
BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
List ids = taskMap.values().stream()
.flatMap(Collection::stream)
.map(Task::getAssignee)
.collect(Collectors.toList());
List longIds = ids.stream()
.map(Long::valueOf)
.collect(Collectors.toList());
// 获得 User Map
Map userMap = adminUserApi.getUserMap(longIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap, userMap);
}
@Override
@Transactional(rollbackFor = Exception.class)
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId());
// 发起流程
return createProcessInstance0(userId, definition, createReqVO.getVariables(), null);
}
@Override
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
// 发起流程
return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey());
}
@Override
public BpmProcessInstanceRespVO getProcessInstanceVO(String id) {
// 获得流程实例
HistoricProcessInstance processInstance = getHistoricProcessInstance(id);
if (processInstance == null) {
return null;
}
BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id);
Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id);
// 获得流程定义
ProcessDefinition processDefinition = processDefinitionService
.getProcessDefinition(processInstance.getProcessDefinitionId());
Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId());
BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt(
processInstance.getProcessDefinitionId());
Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id);
String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId());
// 获得 User
AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData();
DeptRespDTO dept = null;
if (startUser != null) {
dept = deptApi.getDept(startUser.getDeptId()).getCheckedData();
}
// 拼接结果
return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt,
processDefinition, processDefinitionExt, bpmnXml, startUser, dept);
}
@Override
public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) {
// 校验流程实例存在
ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
if (instance == null) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
}
// 只能取消自己的
if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
}
// 通过删除流程实例,实现流程实例的取消,
// 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询
deleteProcessInstance(cancelReqVO.getId(),
BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason()));
}
/**
* 获得历史的流程实例
*
* @param id 流程实例的编号
* @return 历史的流程实例
*/
@Override
public HistoricProcessInstance getHistoricProcessInstance(String id) {
return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult();
}
@Override
public List getHistoricProcessInstances(Set ids) {
return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list();
}
@Override
public void createProcessInstanceExt(ProcessInstance instance) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId());
// 插入 BpmProcessInstanceExtDO 对象
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(instance.getId())
.setProcessDefinitionId(definition.getId())
.setName(instance.getProcessDefinitionName())
.setStartUserId(Long.valueOf(instance.getStartUserId()))
.setCategory(definition.getCategory())
.setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus())
.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
processInstanceExtMapper.insert(instanceExtDO);
}
@Override
public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) {
// 判断是否为 Reject 不通过。如果是,则不进行更新.
// 因为,updateProcessInstanceExtReject 方法,已经进行更新了
if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String)event.getCause())) {
return;
}
// 需要主动查询,因为 instance 只有 id 属性
// 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId());
// 更新拓展表
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(event.getProcessInstanceId())
.setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.CANCEL.getResult());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
@Override
public void updateProcessInstanceExtComplete(ProcessInstance instance) {
// 需要主动查询,因为 instance 只有 id 属性
// 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId());
// 更新拓展表
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(instance.getProcessInstanceId())
.setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
Map processVariables = runtimeService.getVariables(instance.getProcessInstanceId());
String reason = (String)processVariables.get("approve_reason") ;
// 发送流程被通过的消息
messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance,reason));
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateProcessInstanceExtReject(String id, String reason) {
// 需要主动查询,因为 instance 只有 id 属性
ProcessInstance processInstance = getProcessInstance(id);
// 删除流程实例,以实现驳回任务时,取消整个审批流程
deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason)));
// 更新 status + result
// 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法,
// 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id)
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.REJECT.getResult());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
// 发送流程被不通过的消息
messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason));
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
private void deleteProcessInstance(String id, String reason) {
runtimeService.deleteProcessInstance(id, reason);
}
private String createProcessInstance0(Long userId, ProcessDefinition definition,
Map variables, String businessKey) {
// 校验流程定义
if (definition == null) {
throw exception(PROCESS_DEFINITION_NOT_EXISTS);
}
if (definition.isSuspended()) {
throw exception(PROCESS_DEFINITION_IS_SUSPENDED);
}
// 创建流程实例
ProcessInstance instance = runtimeService.createProcessInstanceBuilder()
.processDefinitionId(definition.getId())
.businessKey(businessKey)
.name(definition.getName().trim())
.variables(variables)
.start();
// 设置流程名字
runtimeService.setProcessInstanceName(instance.getId(), definition.getName());
// 补全流程实例的拓展表
processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId())
.setFormVariables(variables));
/** 创建流程后,添加抄送人 End add by yj 2024.1.4 */
processCCToUsers(definition,instance) ;
/** 通过自己发起的流程 */
approveSelfTask(instance.getId()) ;
return instance.getId();
}
private void approveSelfTask(String processInstanceId ) {
List tasks =engineTaskService.createTaskQuery().processInstanceId(processInstanceId).list() ;
if( tasks != null && tasks.size() > 0) {
Task task = tasks.get(0) ;
String assigneeId = task.getAssignee();
//如果当前登陆用户是审批人,那么自动审批通过
if( assigneeId.equals( SecurityFrameworkUtils.getLoginUserId().toString() )) {
BpmTaskApproveReqVO reqVO = new BpmTaskApproveReqVO() ;
reqVO.setId(task.getId()) ;
reqVO.setReason(BpmConstants.AUTO_APPRAVAL);
taskService.approveTask(getLoginUserId(), reqVO);
}
}
}
/**
* 获得抄送我的流程实例的分页
*
* @param userId 用户编号
* @param pageReqVO 分页请求
* @return 流程实例的分页
*/
public PageResult getMyCCProcessInstancePage(Long userId,
BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectCCPage(userId, pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap,null);
}
public List getProcessInstancesGroupByModelName(BpmProcessInstanceStatisticsReqVO pageReqVO){
pageReqVO = getUserids(pageReqVO) ;
return processInstanceExtMapper.getProcessInstancesGroupByModelName(pageReqVO) ;
}
public List getProcessInstancesGroupByResultStatus(BpmProcessInstanceStatisticsReqVO pageReqVO){
pageReqVO = getUserids(pageReqVO) ;
return processInstanceExtMapper.getProcessInstancesGroupByResultStatus(pageReqVO) ;
}
public PageResult getStatisticsProcessInstancePage(@Valid BpmProcessInstanceMyPageReqVO pageReqVO) {
pageReqVO = getUserids(pageReqVO) ;
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectStatisticePage(pageReqVO.getUserIds(), pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
List ids = taskMap.values().stream()
.flatMap(Collection::stream)
.map(Task::getAssignee)
.collect(Collectors.toList());
List longIds = ids.stream()
.map(Long::valueOf)
.collect(Collectors.toList());
//获得 审批人 User Map
Map assigneeUserMap = adminUserApi.getUserMap(longIds);
//获得 发起人 User Map
List bpieDOs = pageResult.getList() ;
longIds = new ArrayList<>() ;
for (BpmProcessInstanceExtDO bpieDO: bpieDOs) {
longIds.add(bpieDO.getStartUserId()) ;
}
Map startUserMap = adminUserApi.getUserMap(longIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertStatisticsPage(pageResult, taskMap, assigneeUserMap,startUserMap);
}
@Resource
PermissionApi permissionApi;
/**
* 根据数据权限,查询关联操作的用户IDS
* @param pageReqVO
* @return
* @param
*/
private T getUserids(T pageReqVO) {
try {
Class clazz = (Class) pageReqVO.getClass();
Field idField = clazz.getDeclaredField("userIds");
idField.setAccessible(true); // 设置可访问性
Long[] userIds = null;
Long userId = WebFrameworkUtils.getLoginUserId();
DeptDataPermissionRespDTO deptDataPermission = permissionApi.getDeptDataPermission(userId).getCheckedData();
//查询全部
if (deptDataPermission.getAll()) {
//idField.set(pageReqVO, null); // 设置属性值
return pageReqVO;
}
// 情况二,即不能查看部门,又不能查看自己,则说明 100% 无权限
if (CollUtil.isEmpty(deptDataPermission.getDeptIds())
&& Boolean.FALSE.equals(deptDataPermission.getSelf())) {
//设置成0,一个不存在的用户Id,就查询不到数据了。
userIds = new Long[]{0L};
idField.set(pageReqVO, userIds);
return pageReqVO;
}
//情况三 至查询自己
if (deptDataPermission.getSelf()) {
userIds = new Long[]{userId};
idField.set(pageReqVO, userIds);
return pageReqVO;
}
Set deptIds = deptDataPermission.getDeptIds();
//查询部门关联的用户Id
List users = adminUserApi.getUserListByDeptIds(deptIds).getCheckedData();
List tempList = new ArrayList<>();
for (AdminUserRespDTO user : users) {
Long id = user.getId();
tempList.add(id);
}
tempList.add(userId);
userIds = tempList.stream().toArray(Long[]::new); //将临时的List集合转换成数组集合
idField.set(pageReqVO, userIds);
return pageReqVO;
}catch (Exception exception){
exception.printStackTrace();
throw exception(BPM_SYSTEM_BUG);
}
}
/**
* /创建流程后,添加抄送人 Begin add by yj 2024.1.4
* 在设计流程的时候,需要添加一个任务块 名字必须叫Activity_cc 分配权限的时候,需要选择用户组。
*
* @param definition
* @param instance
*/
private void processCCToUsers(ProcessDefinition definition, ProcessInstance instance) {
//获取bpm_task_assign_reule (Bpm 任务规则表)的流程中有没有配置抄送节点 固定抄送名称为:Activity_cc
String processDefinitionId = definition.getId() ;
List rules = taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, null);
for(BpmTaskAssignRuleDO rule :rules ){
String key = rule.getTaskDefinitionKey() ; //任务名称
Integer type = rule.getType() ;
if( !key.isEmpty() && key.equals(BpmConstants.CC_NAME) && type == 40 ) {
StringBuffer str = new StringBuffer() ;
Set options = rule.getOptions() ;
List list = new ArrayList(options);
for(Long groupId : list) {
//需要根据这个groupId,查询这个组中的用户id
BpmUserGroupDO userGroup = userGroupService.getUserGroup(groupId);
Set userIds = userGroup.getMemberUserIds() ;
List userIdList = new ArrayList(userIds);
for(Long user_id : userIdList) {
str.append("[").append(user_id).append("]") ;
}
}
String ccids = str.toString() ;
//根据processDefinitionId 将ccids保存到bpm_process_instance_ext中的ccids字段
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessDefinitionId(processDefinitionId)
.setCcids(ccids) .setProcessInstanceId(instance.getProcessInstanceId());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
break ;
}
}
}
@Resource
private BpmTaskExtMapper taskExtMapper;
@Override
public List getUserProcessTpo10(BpmProcessInstanceStatisticsReqVO pageReqVO) {
//按审核人分组查询,统计已完成流程数量及平均耗时间
List respVOS = processInstanceExtMapper.getUserProcessTpo10(pageReqVO);
if( respVOS == null || respVOS.size() == 0) {
return null ;
}
List idList = respVOS.stream()
.map(BpmProcessFinishStatisticsRespVO::getUserId)
.collect(Collectors.toList());
//根据userId,查询UserMap
Map userMap = adminUserApi.getUserMap(idList) ;
Long[] idsArray = new Long[userMap.size()];
// 使用 map 的 keySet() 方法获取键集合
Set keys = userMap.keySet();
// 遍历键集合,将每个键添加到数组中
int index = 0;
for (Long key : keys) {
idsArray[index++] = key;
}
pageReqVO.setUserIds(idsArray) ;
//按审核人分组查询,未审批完成的流程数量
List bpmTaskExtDOs = processInstanceExtMapper.selectUnfinishProcessCount( pageReqVO ) ;
//按审核人分组查询,未完成的记录数
Map unFinfishCountCountMap = new HashMap<>();
for (BpmProcessFinishStatisticsRespVO item : bpmTaskExtDOs) {
unFinfishCountCountMap.put(item.getUserId(), item.getUnFinfishCount());
}
respVOS.forEach(respVO -> respVO.setName( userMap.get(respVO.getUserId()).getNickname()));
respVOS.forEach(respVO -> respVO.setUnFinfishCount( unFinfishCountCountMap.get(respVO.getUserId())));
//获取排行榜第一记录的耗时,作为基础数据
double num = Double.parseDouble(respVOS.get(0).getUserTime()); // 先将字符串转换为双精度浮点数
int baseNumber = (int)num; // 再通过类型转换操作符将其转换为整数
DecimalFormat df = new DecimalFormat("#.00");
respVOS.forEach(respVO -> {
//格式化流程完成率
float finfishCount = (float) respVO.getFinfishCount() ;
float unFinfishCount = respVO.getUnFinfishCount() == null ? 0: respVO.getUnFinfishCount();
float all = finfishCount + unFinfishCount ;
float result = finfishCount / all;
float rate = result * 100 ;
respVO.setCompletionRate(df.format(rate)+"%") ;
double dValue = Double.parseDouble(respVO.getUserTime()); // 将字符串转换为double类型
int roundedValue = (int) Math.round(dValue); // 使用Math.round()进行四舍五入,并转换为int类型
respVO.setUserTime(roundedValue+"") ;
//格式化进度百度比, 参照最高的数据进行百分比显示
double percentage = ((int)Double.parseDouble(respVO.getUserTime()) / (double) baseNumber) * 100;
respVO.setPercentage((int) Math.round(percentage)) ;
//设置未完成
respVO.setUnFinfishCount( Integer.valueOf((int)unFinfishCount)) ;
});
return respVOS ;
}
}
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
index e5d685b1..4e6ca728 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
@@ -388,6 +388,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
// 只获配置的首个岗位
Long postId = list.get(0) ;
paramMap.put("post_id",postId.toString()) ;
+ paramMap.put("approve_reason",reqVO.getReason()) ; //通过原因---因为Task任务的通过,是通过监听事件处理的,reason数据无法传递给监听函数。所以通过此中方法传递
taskService.complete(task.getId(), paramMap);
// 更新任务拓展表为通过
taskExtMapper.updateByTaskId(
@@ -688,6 +689,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
BpmTaskExtDO taskExtDO =
new BpmTaskExtDO().setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setTaskId(task.getId());
taskExtMapper.updateByTaskId(taskExtDO);
+
// 发送通知。在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
@@ -696,11 +698,12 @@ public class BpmTaskServiceImpl implements BpmTaskService {
ProcessInstance processInstance =
processInstanceService.getProcessInstance(task.getProcessInstanceId());
if( processInstance != null ) {
+ // 获得任务列表
+ List BpmTaskRespVOs = getTaskListByProcessInstanceId(processInstance.getProcessInstanceId()) ;
AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())).getCheckedData();
messageService.sendMessageWhenTaskAssigned(
- BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
+ BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task, BpmTaskRespVOs));
}
-
}
}
});
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/subscribe/SubscribeMessageSendApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/subscribe/SubscribeMessageSendApi.java
index dbb92056..0509fb8b 100644
--- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/subscribe/SubscribeMessageSendApi.java
+++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/subscribe/SubscribeMessageSendApi.java
@@ -17,9 +17,9 @@ public interface SubscribeMessageSendApi {
@PostMapping(PREFIX + "/send-approval-result-notification")
@Operation(summary = "发送审批结果通知")
- CommonResult sendApprovalResultNotification(SubscribeMessageReqDTO reqDTO);
+ CommonResult sendApprovalResultNotification(@RequestBody SubscribeMessageReqDTO reqDTO);
@PostMapping(PREFIX + "/send-process-todo-reminder")
@Operation(summary = "发送OA流程待办提醒")
- CommonResult sendProcessToDoReminder(SubscribeMessageReqDTO reqDTO);
+ CommonResult sendProcessToDoReminder(@RequestBody SubscribeMessageReqDTO reqDTO);
}
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/subscribe/dto/SubscribeMessageReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/subscribe/dto/SubscribeMessageReqDTO.java
index c5ca1935..cc79bfa8 100644
--- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/subscribe/dto/SubscribeMessageReqDTO.java
+++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/subscribe/dto/SubscribeMessageReqDTO.java
@@ -1,12 +1,10 @@
package cn.iocoder.yudao.module.system.api.subscribe.dto;
import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.AllArgsConstructor;
import lombok.Data;
-import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
-import java.io.Serializable;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -28,10 +26,13 @@ public class SubscribeMessageReqDTO {
private String templateId;
@Schema(description = "所需下发的模板消息的属性集合", requiredMode = Schema.RequiredMode.REQUIRED, example = "OnJjp5pdjG1PHMoELYaqp3Xq8jWZ5E6ndO0clEIQ4tk")
- private List data;
+ private List data = new ArrayList<>();
@Schema(description = "跳转小程序类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "developer为开发版;trial为体验版;formal为正式版;默认为正式版")
@NotNull(message = "跳转小程序类型不能为空")
private String miniprogramState = "formal" ;
+ public void addData(MsgData data) {
+ this.data.add(data) ;
+ }
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/subscribe/SubscribeMessageSendApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/subscribe/SubscribeMessageSendApiImpl.java
index 97bfa0b0..1e543317 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/subscribe/SubscribeMessageSendApiImpl.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/subscribe/SubscribeMessageSendApiImpl.java
@@ -1,17 +1,29 @@
package cn.iocoder.yudao.module.system.api.subscribe;
+import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaSubscribeService;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
+import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
import cn.iocoder.yudao.module.system.api.subscribe.dto.MsgData;
import cn.iocoder.yudao.module.system.api.subscribe.dto.SubscribeMessageReqDTO;
+import cn.iocoder.yudao.module.system.service.social.SocialClientService;
+import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties;
+import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
import lombok.SneakyThrows;
import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
+import me.chanjar.weixin.mp.api.WxMpService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
+import java.time.Duration;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -25,27 +37,29 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class SubscribeMessageSendApiImpl implements SubscribeMessageSendApi{
+
@Resource
- private WxMaSubscribeService wxMaSubscribeService ;
+ private SocialClientService socialClientService;
@SneakyThrows
@Override
public CommonResult sendApprovalResultNotification(SubscribeMessageReqDTO reqDTO) {
- wxMaSubscribeService.sendSubscribeMsg(initWxMaSubscribeMessage(reqDTO));
+ socialClientService.getWxMaService().getMsgService().sendSubscribeMsg(initWxMaSubscribeMessage(reqDTO));
return success(1L);
}
@SneakyThrows
@Override
public CommonResult sendProcessToDoReminder(SubscribeMessageReqDTO reqDTO) {
- wxMaSubscribeService.sendSubscribeMsg(initWxMaSubscribeMessage(reqDTO));
+ socialClientService.getWxMaService().getMsgService().sendSubscribeMsg(initWxMaSubscribeMessage(reqDTO));
return success(1L);
}
private WxMaSubscribeMessage initWxMaSubscribeMessage( SubscribeMessageReqDTO reqDTO ) {
- WxMaSubscribeMessage message = new WxMaSubscribeMessage() ;
- message.setToUser(reqDTO.getToUser()) ;
- message.setTemplateId(reqDTO.getTemplateId());
+ WxMaSubscribeMessage message = WxMaSubscribeMessage.builder()
+ .toUser(reqDTO.getToUser())
+ .templateId(reqDTO.getTemplateId())
+ .build();
message.setMiniprogramState(reqDTO.getMiniprogramState());
List dataList = reqDTO.getData() ;
for (MsgData msgData : dataList) {
@@ -54,6 +68,8 @@ public class SubscribeMessageSendApiImpl implements SubscribeMessageSendApi{
wxMsgData.setValue(msgData.getValue()) ;
message.addData(wxMsgData) ;
}
+ message.setPage("pages/home/index") ;
return message ;
}
+
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
index e3dc7e5c..21ccd57a 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
@@ -264,7 +264,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
//获取微信小程序授权手机号
WxMaPhoneNumberInfo wxMaPhoneNumberInfo = socialClientService.getWxMaPhoneNumberInfo(2,reqVO.getCode()) ;
String phoneNumber = wxMaPhoneNumberInfo.getPhoneNumber() ; //授权手机号
- phoneNumber = "18611845857" ;
+ //phoneNumber = "18611845857" ;
String appId = wxMaPhoneNumberInfo.getWatermark().getAppid() ; //小程序的appId
final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_MOBILE;
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java
index 513e2546..7756ce4a 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.system.service.social;
+import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -69,6 +70,12 @@ public interface SocialClientService {
*/
WxMaJscode2SessionResult getWxMaJscode2SessionResult(String jsCode) ;
+ /**
+ * 获取WxMaService
+ * @return
+ */
+ WxMaService getWxMaService() ;
+
// =================== 客户端管理 ===================
/**
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java
index 36df66ad..cc60e0f2 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java
@@ -92,6 +92,11 @@ public class SocialClientServiceImpl implements SocialClientService {
private WxMaService wxMaService;
@Resource
private WxMaProperties wxMaProperties;
+
+ @Override
+ public WxMaService getWxMaService() {
+ return getWxMaService(2) ;
+ }
/**
* 缓存 WxMaService 对象
*
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/application-local.yaml b/yudao-module-system/yudao-module-system-biz/src/main/resources/application-local.yaml
index babcc5f6..c74890d1 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/resources/application-local.yaml
+++ b/yudao-module-system/yudao-module-system-biz/src/main/resources/application-local.yaml
@@ -143,8 +143,8 @@ wx:
miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
# appid: wx62056c0d5e8db250
# secret: 333ae72f41552af1e998fe1f54e1584a
- appid: wxd0261c48ca0c6df3 # wenhualian的接口测试号
- secret: eaa3996319c595b5ece50e4ed9261114
+ appid: wx2919e237e6018bea # wenhualian的接口测试号
+ secret: ad7ab17918f6defa85a9677778eb7780
config-storage:
type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
key-prefix: wa # Redis Key 的前缀
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/application-prod.yaml b/yudao-module-system/yudao-module-system-biz/src/main/resources/application-prod.yaml
index 20fe494b..dbaa7f96 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/resources/application-prod.yaml
+++ b/yudao-module-system/yudao-module-system-biz/src/main/resources/application-prod.yaml
@@ -143,8 +143,8 @@ wx:
miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
# appid: wx62056c0d5e8db250
# secret: 333ae72f41552af1e998fe1f54e1584a
- appid: wx63c280fe3248a3e7 # wenhualian的接口测试号
- secret: 6f270509224a7ae1296bbf1c8cb97aed
+ appid: wx2919e237e6018bea # wenhualian的接口测试号
+ secret: ad7ab17918f6defa85a9677778eb7780
config-storage:
type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
key-prefix: wa # Redis Key 的前缀