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 的前缀