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 26d98524..cbedd2ef 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,1172 @@
-package cn.iocoder.yudao.module.bpm.service.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
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.oa.vo.cash.BpmOACashRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.payment.BpmOAPaymentRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmOAPrintDataRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmProcessInstancePrintDataReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmProcessInstancePrintDataRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.reimbursement.BpmOAReimbursementRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.salary.BpmOASalaryRespVO;
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.controller.admin.task.vo.task.BpmTaskRespVO;
import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessCcDO;
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.oa.*;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.DynamicMapper;
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.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.BpmProcessCcService;
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.bpm.service.oa.*;
import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.smartfactory.api.factoryInfo.FactoryInfoApi;
import cn.iocoder.yudao.module.smartfactory.api.factoryInfo.dto.FactoryInfoDTO;
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.update.LambdaUpdateWrapper;
import jodd.util.StringUtil;
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.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
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.time.temporal.ChronoUnit;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.convertList;
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
@Lazy // 解决循环依赖
private BpmMessageService messageService;
@Resource
private ConfigApi configApi;
@Resource
private DynamicMapper dynamicMapper ;
@Resource
private BpmProcessCcService processCcService;
@Resource
@Lazy // 解决循环依赖
private StringRedisTemplate stringRedisTemplate;
@Override
public ProcessInstance getProcessInstance(String id) {
return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult();
}
@Override
public List getProcessInstances(Set ids) {
return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
}
public String transform(String input) {
if(StringUtil.isNotEmpty(input)) {
String[] parts = input.split(",");
StringBuilder result = new StringBuilder();
for (String part : parts) {
String[] keyValue = part.split(":");
if (keyValue.length > 1) {
result.append(keyValue[1]).append(",");
}
}
if (result.length() > 0) {
return result.substring(0, result.length() - 1);
}
}
return "";
}
public String replaceValues(String input, Map map) {
String[] parts = input.split(",");
StringBuilder result = new StringBuilder();
for (String part : parts) {
String[] keyValue = part.split(":");
if (keyValue.length > 1) {
String key = keyValue[1].trim(); // 去除可能的空格
if (map.containsKey(key)) {
Object value = map.get(key);
String stringValue = (value != null) ? value.toString() : ""; // 将值转换为字符串
result.append(keyValue[0].trim()).append(":").append(stringValue).append(",");
} else {
//result.append(part).append(",");
}
}
}
if (result.length() > 0) {
return result.substring(0, result.length() - 1);
}
return "";
}
@Override
@TenantIgnore
public PageResult getMyProcessInstancePage(Long userId,
BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO);
List bpmProcessInstanceExtDOList = pageResult.getList() ;
for (int i = 0; i < bpmProcessInstanceExtDOList.size() ; i++) {
BpmProcessInstanceExtDO bpmProcessInstanceExtDO = bpmProcessInstanceExtDOList.get(i) ;
String input = bpmProcessInstanceExtDO.getFieldNames() ;
if(StringUtil.isNotEmpty(input)) {
String sql = "SELECT " + transform(bpmProcessInstanceExtDO.getFieldNames()) + " FROM " + bpmProcessInstanceExtDO.getTableName() + " WHERE id=" + bpmProcessInstanceExtDO.getBusinessKey();
log.info(sql);
Map map = dynamicMapper.selectListByTableName(sql) ;
String detailInfo = "";
if(map != null) {
detailInfo = replaceValues(bpmProcessInstanceExtDO.getFieldNames(),map) ;
}
bpmProcessInstanceExtDO.setDetailInfo(detailInfo);
}
}
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());
Iterator iterator = ids.iterator();
while (iterator.hasNext()) {
String id = iterator.next();
if (id == null) {
iterator.remove();
}
}
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());
// 发起流程
if (MapUtil.isEmpty(createReqVO.getVariables())){
createReqVO.setVariables(new HashMap<>());
}
return createProcessInstance0(userId, definition, createReqVO.getVariables(), null);
}
@Override
@Transactional(rollbackFor = Exception.class)
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
// 发起流程
if (MapUtil.isEmpty(createReqDTO.getVariables())){
createReqDTO.setVariables(new HashMap<>());
}
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;
DeptRespDTO companyDept = null;
if (startUser != null) {
dept = deptApi.getDept(startUser.getDeptId()).getCheckedData();
companyDept = deptApi.getUserCompanyDept(startUser.getId()).getCheckedData();
}
// 拼接结果
return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt,
processDefinition, processDefinitionExt, bpmnXml, startUser, dept, companyDept);
}
@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
@DataPermission(enable = false)
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);
// 删除 流程审批部门缓存
updateAssigneeDeptRedis(event.getProcessInstanceId());
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
@Override
@DataPermission(enable = false)
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);
// 删除 流程审批部门缓存
updateAssigneeDeptRedis(instance.getProcessInstanceId());
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)
@DataPermission(enable = false)
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)
.setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.REJECT.getResult());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
// 删除 流程审批部门缓存
updateAssigneeDeptRedis(id);
// 发送流程被不通过的消息
messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason));
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
@Override
public void updateProcessInstanceResult(String id, Integer result) {
processInstanceExtMapper.update(null, new LambdaUpdateWrapper()
.set(BpmProcessInstanceExtDO::getResult, result)
.eq(BpmProcessInstanceExtDO::getProcessInstanceId, id));
}
/**
* 删除 流程审批部门缓存
* @param processInstanceId 流程实例id
*/
public void updateAssigneeDeptRedis(String processInstanceId) {
stringRedisTemplate.delete("assignee_dept_" + processInstanceId);
}
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);
}
// 设置当前用户
FlowableUtils.setAuthenticatedUserId(userId);
//获取流程发起人User
AdminUserRespDTO startUser = adminUserApi.getUser(userId).getCheckedData();
//获取流程发起人部门信息
DeptRespDTO startDeptInfo = deptApi.getDept(startUser.getDeptId()).getCheckedData();
// 获取岗位信息
Set postIds = startUser.getPostIds();
ArrayList list = new ArrayList<>(postIds);
if (CollectionUtil.isEmpty(startUser.getPostIds())) {
// 当前审批用户未配置岗位
// 操作失败,原因:您未配置岗位,请联系管理员操作
throw exception(TASK_OPERATE_FAIL_USER_NO_POST);
}
if( startUser.getDeptId() == null ) {
// 当前审批用户未配置部门
// 操作失败,原因:您未配置部门,请联系管理员操作
throw exception(TASK_OPERATE_FAIL_USER_NO_DEPT);
}
//配置通用流程变量
variables.put("post_id", list.get(0).toString()); // 只获配置的首个岗位
variables.put("user_id", userId.toString()); // 配置发起人用户id
variables.put("dept_id", startUser.getDeptId().toString()); // 配置发起人部门id
variables.put("dept_flag", startDeptInfo.getFlag()); // 配置发起人部门flag
// 创建流程实例
ProcessInstance instance = runtimeService.createProcessInstanceBuilder()
.processDefinitionId(definition.getId())
.businessKey(businessKey)
.name(definition.getName().trim())
.variables(variables)
.start();
// 设置流程名字
runtimeService.setProcessInstanceName(instance.getId(), definition.getName());
// 设置流程审批部门缓存
stringRedisTemplate.opsForValue().set("assignee_dept_" + instance.getId(), startUser.getDeptId().toString());
// // 在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。
// TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
//
// @Override
// public void afterCommit() {
//
// // 创建流程后,添加抄送人
// processCCToUsers(instance, variables);
// }
// });
// 创建流程后,添加抄送人
processCCToUsers(instance, variables);
return instance.getId();
// // 补全流程实例的拓展表
// processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId())
// .setFormVariables(variables));
//
// /** 创建流程后,添加抄送人 End add by yj 2024.1.4 */
// processCCToUsers(definition, instance);
// /** 通过自己发起的流程 */
// approveSelfTask(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);
}
@Override
public List getProcessInstancesGroupByModelName(BpmProcessInstanceStatisticsReqVO pageReqVO) {
pageReqVO = getUserids(pageReqVO);
return processInstanceExtMapper.getProcessInstancesGroupByModelName(pageReqVO);
}
@Override
public List getProcessInstancesGroupByResultStatus(BpmProcessInstanceStatisticsReqVO pageReqVO) {
pageReqVO = getUserids(pageReqVO);
return processInstanceExtMapper.getProcessInstancesGroupByResultStatus(pageReqVO);
}
@Override
public PageResult getStatisticsProcessInstancePage(@Valid BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectStatisticePage(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());
Iterator iterator = ids.iterator();
while (iterator.hasNext()) {
String id = iterator.next();
if (id == null) {
iterator.remove();
}
}
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
* @param
* @return
*/
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);
String BpmConstantsName = BpmConstants.CC_NAME;
// 获取发起人信息
AdminUserRespDTO userRespDTO = adminUserApi.getUser(Long.valueOf(instance.getStartUserId())).getCheckedData();
DeptRespDTO deptRespDTO = deptApi.getDept(userRespDTO.getDeptId()).getCheckedData();
//发起人是深圳分公司
if (deptRespDTO.getFlag().contains("136")) {
BpmConstantsName = BpmConstants.CCSZ_NAME;
}
for (BpmTaskAssignRuleDO rule : rules) {
String key = rule.getTaskDefinitionKey(); //任务名称
Integer type = rule.getType();
if (!key.isEmpty() && key.equals(BpmConstantsName) && 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("]");
}
}
//流程是采购计划时
if (definition.getKey().equals(BpmOAProcureServiceImpl.PROCESS_KEY)) {
//发起人部门为 生产部及以下时
if (deptRespDTO.getFlag().contains("130")) {
//添加 供应部抄送
BpmUserGroupDO userGroup = userGroupService.getUserGroup(121L);
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;
}
}
}
/**
* 创建流程后,添加抄送人
* @param instance 流程实例
*/
private void processCCToUsers(ProcessInstance instance, Map variables) {
// 根据流程名称、流程发起人 查询流程配置的抄送人信息
List processCcList = processCcService.getCCListByName(instance.getName(), Long.valueOf(instance.getStartUserId()));
// 提取抄送信息用中对应的用户组编号
Set userGroupIds = processCcList.stream()
.flatMap(data -> data.getUserGroupId().stream())
.collect(Collectors.toSet());
//根据processDefinitionId 将ccIds保存到bpm_process_instance_ext中的ccIds字段
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessDefinitionId(instance.getProcessDefinitionId())
.setFormVariables(variables)
.setProcessInstanceId(instance.getProcessInstanceId());
if (CollectionUtil.isNotEmpty(userGroupIds)) {
// 获取用户组信息
List userGroups = userGroupService.getUserGroupList(userGroupIds);
// 提取用户组中对应的用户ID
Set userIds = userGroups.stream()
.flatMap(data -> data.getMemberUserIds().stream())
.collect(Collectors.toSet());
// 将用户ID列表转换为字符串
String ccIds = userIds.stream()
.map(id -> "[" + id + "]") // 每个值用方括号包裹
.collect(Collectors.joining());
//根据processDefinitionId 将ccIds保存到bpm_process_instance_ext中的ccIds字段
instanceExtDO.setCcids(ccIds);
}
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
}
@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;
}
@Override
@DataPermission(enable = false)
public BpmProcessInstanceExtDO getProcessInstanceDO(String id) {
return processInstanceExtMapper.selectByProcessInstanceId(id);
}
@Resource
private FileApi fileApi;
@Resource
@Lazy // 解决循环依赖
private BpmOAReimbursementService reimbursementService;
@Resource
@Lazy // 解决循环依赖
private BpmOACashService cashService;
@Resource
@Lazy // 解决循环依赖
private BpmOAImprestService imprestService;
@Resource
@Lazy // 解决循环依赖
private BpmOAPaymentService paymentService;
@Resource
@Lazy
private BpmOASalaryService salaryService;
@Resource
private FactoryInfoApi factoryInfoApi;
@Override
public BpmProcessInstancePrintDataRespVO getOAReportPrintData(BpmProcessInstancePrintDataReqVO reqVO) {
BpmProcessInstancePrintDataRespVO bpmProcessInstancePrintDataRespVO = new BpmProcessInstancePrintDataRespVO();
String key = reqVO.getKey(); //流程标识
String processInstanceId = reqVO.getId(); //流程实例ID
bpmProcessInstancePrintDataRespVO.setId(processInstanceId);
bpmProcessInstancePrintDataRespVO.setKey(key);
BpmProcessInstanceRespVO bpmProcessInstance = getProcessInstanceVO(processInstanceId);
Long businessKey = Long.valueOf(bpmProcessInstance.getBusinessKey()); //流程业务表-主键ID
// 判断是否可见外勤人员信息
List userIds = new ArrayList<>();
String type = configApi.getConfigKey("sys.user.type").getCheckedData();
if ("1".equals(type)) { //不可见时
// 获取所有外勤人员 用户编号
userIds = adminUserApi.getUserIdsByUserNature(4).getCheckedData();
}
// 获得流程审批信息
List taskRespVOList = taskService.getTaskListByProcessInstanceId(processInstanceId);
// 移除外勤人员信息
List finalUserIds = userIds;
taskRespVOList.removeIf(data -> finalUserIds.contains(data.getAssigneeUser().getId()));
//给User添加签名地址
taskRespVOList.forEach(taskRespVO ->
taskRespVO.getAssigneeUser().setSignURL(
fileApi.getUserSignImgPath(
taskRespVO.getAssigneeUser().getId()
).getData()
)
);
//获取流程抄送用户编号
BpmProcessInstanceExtDO processInstanceExtDO = getProcessInstanceDO(processInstanceId);
//获取流程抄送用户信息
List ccUserIds = new ArrayList<>();
if (processInstanceExtDO.getCcids() != null && !processInstanceExtDO.getCcids().isEmpty()) {
Pattern pattern = Pattern.compile("\\[(\\d+)]");
Matcher matcher = pattern.matcher(processInstanceExtDO.getCcids());
while (matcher.find()) {
ccUserIds.add(Long.parseLong(matcher.group(1)));
}
// 移除 外勤人员信息
ccUserIds.removeAll(userIds);
}
List userRespDTOS = adminUserApi.getUserList(ccUserIds).getCheckedData();
//获取抄送用户部门信息
Map deptMapDTO = deptApi.getDeptMap(convertSet(userRespDTOS, AdminUserRespDTO::getDeptId));
// 设置抄送人信息
List ccUsers = userRespDTOS.stream().map(user -> {
BpmOAPrintDataRespVO.CCUser cc = new BpmOAPrintDataRespVO.CCUser();
cc.setCcUserId(user.getId());
cc.setCcUserName(user.getNickname());
cc.setCcDeptName(deptMapDTO.get(user.getDeptId()).getName());
return cc;
}).collect(Collectors.toList());
BpmOAPrintDataRespVO printData = new BpmOAPrintDataRespVO();
// 设置抄送人
printData.setCcUsers(ccUsers);
// 设置发起人信息
printData.setStartUser(BeanUtils.toBean(bpmProcessInstance.getStartUser(), BpmOAPrintDataRespVO.User.class));
switch (key) {
case "oa_reimbursement":
BpmOAReimbursementDO reimbursement = reimbursementService.getReimbursement(businessKey);
BpmOAReimbursementRespVO bpmOAReimbursementRespVO = reimbursementService.convert(reimbursement); //报销业务数据
// 移除自己得审批节点
taskRespVOList.removeIf(data -> data.getAssigneeUser().getId().equals(reimbursement.getUserId()));
//备用金信息查询
BpmOAImprestDO bpmOAImprestDO = imprestService.getImprest(reimbursement.getImprestId());
if (bpmOAImprestDO != null) {
//设置备用金 金额
bpmOAReimbursementRespVO.setAmount(bpmOAImprestDO.getAmount());
// 设置备用金 剩余金额
bpmOAReimbursementRespVO.setRemainingAmount(bpmOAImprestDO.getAmount().subtract(bpmOAImprestDO.getReimbursedAmount()));
}
printData.setBpmOAReimbursementRespVO(bpmOAReimbursementRespVO);
printData.setProcessTasks(taskRespVOList);
break;
case "oa_cash":
BpmOACashDO cashDO = cashService.getCash(businessKey);
BpmOACashRespVO cashRespVO = cashService.convertCash(cashDO);
// 移除自己得审批节点
taskRespVOList.removeIf(data -> data.getAssigneeUser().getId().equals(cashDO.getUserId()));
//备用金信息查询
BpmOAImprestDO cashImprestDO = imprestService.getImprest(cashDO.getImprestId());
if (cashImprestDO != null) {
//设置备用金 金额
cashRespVO.setAmount(cashImprestDO.getAmount());
// 设置备用金 剩余金额
cashRespVO.setRemainingAmount(cashImprestDO.getAmount().subtract(cashImprestDO.getReimbursedAmount()));
}
printData.setBpmOACashRespVO(cashRespVO);
printData.setProcessTasks(taskRespVOList);
break;
case "oa_payment_2":
BpmOAPaymentDO paymentDO = paymentService.getPayment(businessKey);
BpmOAPaymentRespVO paymentRespVO = paymentService.convertPayment(paymentDO);
// 移除自己得审批节点
taskRespVOList.removeIf(data -> data.getAssigneeUser().getId().equals(paymentDO.getUserId()));
printData.setBpmOAPaymentRespVO(paymentRespVO);
printData.setProcessTasks(taskRespVOList);
break;
case "oa_salary_2":
BpmOASalaryDO salaryDO = salaryService.getSalary(businessKey);
BpmOASalaryRespVO salaryRespVO = BeanUtils.toBean(salaryDO, BpmOASalaryRespVO.class);
if (salaryDO.getCompanyDeptId() != null) {
// 获取部门详情
DeptRespDTO dto = deptApi.getDept(salaryDO.getCompanyDeptId()).getCheckedData();
salaryRespVO.setCompanyName(dto.getName());
}else {
// 获取工厂信息
Set factoryIds = salaryDO.getFactoryDeptId();
List factoryInfoDTOS = factoryInfoApi.getFactoryInfoList(factoryIds).getCheckedData();
String factoryNames = factoryInfoDTOS.stream()
.map(FactoryInfoDTO::getShortName)
.filter(Objects::nonNull)
.collect(Collectors.joining(","));
salaryRespVO.setCompanyName(factoryNames);
}
// 移除自己得审批节点
taskRespVOList.removeIf(data -> data.getAssigneeUser().getId().equals(salaryDO.getUserId()));
printData.setBpmOASalaryRespVO(salaryRespVO);
printData.setProcessTasks(taskRespVOList);
break;
}
bpmProcessInstancePrintDataRespVO.setPrintDataRespVO(printData);
return bpmProcessInstancePrintDataRespVO;
}
@Override
public Map> getProcessInstanceResultStatusStatisticsGroupTime(BpmProcessInstanceStatisticsReqVO pageReqVO) {
pageReqVO = getUserids(pageReqVO);
pageReqVO.setRecentDays(pageReqVO.getRecentDays() == null ? 7 : pageReqVO.getRecentDays());
List list = processInstanceExtMapper.getProcessInstanceResultStatusStatisticsGroupTime(pageReqVO);
// -- 获取到日期列表
LocalDateTime now = LocalDateTimeUtil.now();
LocalDateTime offset = LocalDateTimeUtil.offset(LocalDateTimeUtil.now(), -1L * pageReqVO.getRecentDays(), ChronoUnit.DAYS);
List dateList = DateUtils.betweenDayList(offset, now);
//根据时间分组
Map> map = list.stream().collect(Collectors.groupingBy(BpmProcessInstanceResultStatusStatisticsGroupTimeVO::getTime));
List keys = new ArrayList<>(CollectionUtil.subtract(dateList, new ArrayList<>(map.keySet())));
if (CollectionUtil.isNotEmpty(keys)) {
for (String key : keys) {
map.put(key, new ArrayList<>());
}
}
List statusList = Arrays.asList(1, 2, 3, 4);
for (Map.Entry> entry : map.entrySet()) {
List items = entry.getValue();
List results = items.stream().map(BpmProcessInstanceResultStatusStatisticsGroupTimeVO::getResult).collect(Collectors.toList());
List saveList = new ArrayList<>(CollectionUtil.subtract(statusList, results));
// -- 如果没有的话组装上缺少的
if (CollectionUtil.isNotEmpty(saveList)) {
for (Integer status : saveList) {
items.add(new BpmProcessInstanceResultStatusStatisticsGroupTimeVO()
.setTime(entry.getKey())
.setTotalCount(0)
.setResult(status)
.setName(""));
}
}
}
return map;
}
}
\ No newline at end of file
+package cn.iocoder.yudao.module.bpm.service.task;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.date.DateUtils;
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
+import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
+import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
+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.oa.vo.cash.BpmOACashRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.payment.BpmOAPaymentRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmOAPrintDataRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmProcessInstancePrintDataReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmProcessInstancePrintDataRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.reimbursement.BpmOAReimbursementRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.salary.BpmOASalaryRespVO;
+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.controller.admin.task.vo.task.BpmTaskRespVO;
+import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessCcDO;
+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.oa.*;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
+import cn.iocoder.yudao.module.bpm.dal.mysql.DynamicMapper;
+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.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.BpmProcessCcService;
+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.bpm.service.oa.*;
+import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
+import cn.iocoder.yudao.module.infra.api.file.FileApi;
+import cn.iocoder.yudao.module.smartfactory.api.factoryInfo.FactoryInfoApi;
+import cn.iocoder.yudao.module.smartfactory.api.factoryInfo.dto.FactoryInfoDTO;
+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.update.LambdaUpdateWrapper;
+import jodd.util.StringUtil;
+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.springframework.context.annotation.Lazy;
+import org.springframework.data.redis.core.StringRedisTemplate;
+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.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+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.convertList;
+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.*;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * 流程实例 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
+ @Lazy // 解决循环依赖
+ private BpmMessageService messageService;
+
+ @Resource
+ private ConfigApi configApi;
+
+ @Resource
+ private DynamicMapper dynamicMapper ;
+
+ @Resource
+ private BpmProcessCcService processCcService;
+
+ @Resource
+ @Lazy // 解决循环依赖
+ private StringRedisTemplate stringRedisTemplate;
+
+ @Override
+ public ProcessInstance getProcessInstance(String id) {
+ return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult();
+ }
+
+ @Override
+ public List getProcessInstances(Set ids) {
+ return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
+ }
+
+ public String transform(String input) {
+
+
+ if(StringUtil.isNotEmpty(input)) {
+ String[] parts = input.split(",");
+ StringBuilder result = new StringBuilder();
+ for (String part : parts) {
+ String[] keyValue = part.split(":");
+ if (keyValue.length > 1) {
+ result.append(keyValue[1]).append(",");
+ }
+ }
+ if (result.length() > 0) {
+ return result.substring(0, result.length() - 1);
+ }
+ }
+ return "";
+ }
+
+ public String replaceValues(String input, Map map) {
+ String[] parts = input.split(",");
+ StringBuilder result = new StringBuilder();
+ for (String part : parts) {
+ String[] keyValue = part.split(":");
+ if (keyValue.length > 1) {
+ String key = keyValue[1].trim(); // 去除可能的空格
+ if (map.containsKey(key)) {
+ Object value = map.get(key);
+ String stringValue = (value != null) ? value.toString() : ""; // 将值转换为字符串
+ result.append(keyValue[0].trim()).append(":").append(stringValue).append(",");
+ } else {
+ //result.append(part).append(",");
+ }
+ }
+ }
+ if (result.length() > 0) {
+ return result.substring(0, result.length() - 1);
+ }
+ return "";
+ }
+
+ @Override
+ @TenantIgnore
+ public PageResult getMyProcessInstancePage(Long userId,
+ BpmProcessInstanceMyPageReqVO pageReqVO) {
+ // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
+ PageResult pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO);
+
+ List bpmProcessInstanceExtDOList = pageResult.getList() ;
+ for (int i = 0; i < bpmProcessInstanceExtDOList.size() ; i++) {
+ BpmProcessInstanceExtDO bpmProcessInstanceExtDO = bpmProcessInstanceExtDOList.get(i) ;
+ String input = bpmProcessInstanceExtDO.getFieldNames() ;
+ if(StringUtil.isNotEmpty(input)) {
+ String sql = "SELECT " + transform(bpmProcessInstanceExtDO.getFieldNames()) + " FROM " + bpmProcessInstanceExtDO.getTableName() + " WHERE id=" + bpmProcessInstanceExtDO.getBusinessKey();
+ log.info(sql);
+ Map map = dynamicMapper.selectListByTableName(sql) ;
+ String detailInfo = "";
+ if(map != null) {
+ detailInfo = replaceValues(bpmProcessInstanceExtDO.getFieldNames(),map) ;
+ }
+ bpmProcessInstanceExtDO.setDetailInfo(detailInfo);
+ }
+ }
+
+ 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());
+
+ Iterator iterator = ids.iterator();
+ while (iterator.hasNext()) {
+ String id = iterator.next();
+ if (id == null) {
+ iterator.remove();
+ }
+ }
+
+ 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());
+ // 发起流程
+ if (MapUtil.isEmpty(createReqVO.getVariables())){
+ createReqVO.setVariables(new HashMap<>());
+ }
+ return createProcessInstance0(userId, definition, createReqVO.getVariables(), null);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {
+ // 获得流程定义
+ ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
+ // 发起流程
+ if (MapUtil.isEmpty(createReqDTO.getVariables())){
+ createReqDTO.setVariables(new HashMap<>());
+ }
+ 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;
+ DeptRespDTO companyDept = null;
+ if (startUser != null) {
+ dept = deptApi.getDept(startUser.getDeptId()).getCheckedData();
+ companyDept = deptApi.getUserCompanyDept(startUser.getId()).getCheckedData();
+ }
+
+ // 拼接结果
+ return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt,
+ processDefinition, processDefinitionExt, bpmnXml, startUser, dept, companyDept);
+ }
+
+ @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);
+
+ // 在记录创建后立即处理抄送人信息(双重保险)
+ handleProcessInstanceCC(instance);
+ }
+
+ /**
+ * 在事件监听器中处理抄送人信息
+ */
+ private void handleProcessInstanceCC(ProcessInstance instance) {
+ try {
+ // 获取流程变量
+ Map variables = runtimeService.getVariables(instance.getId());
+
+ // 根据流程名称、流程发起人 查询流程配置的抄送人信息
+ List processCcList = processCcService.getCCListByName(instance.getName(), Long.valueOf(instance.getStartUserId()));
+ // 提取抄送信息用中对应的用户组编号
+ Set userGroupIds = processCcList.stream()
+ .flatMap(data -> data.getUserGroupId().stream())
+ .collect(Collectors.toSet());
+
+ if (CollectionUtil.isNotEmpty(userGroupIds)) {
+ // 获取用户组信息
+ List userGroups = userGroupService.getUserGroupList(userGroupIds);
+ // 提取用户组中对应的用户ID
+ Set userIds = userGroups.stream()
+ .flatMap(data -> data.getMemberUserIds().stream())
+ .collect(Collectors.toSet());
+
+ // 将用户ID列表转换为字符串
+ String ccIds = userIds.stream()
+ .map(id -> "[" + id + "]") // 每个值用方括号包裹
+ .collect(Collectors.joining());
+
+ // 直接更新刚创建的记录
+ BpmProcessInstanceExtDO updateDO = new BpmProcessInstanceExtDO()
+ .setProcessInstanceId(instance.getId())
+ .setFormVariables(variables)
+ .setCcids(ccIds);
+
+ processInstanceExtMapper.updateByProcessInstanceId(updateDO);
+ log.debug("事件监听器中抄送人信息设置成功,processInstanceId: {}", instance.getId());
+ }
+ } catch (Exception e) {
+ log.error("事件监听器中处理抄送人信息失败,processInstanceId: {}", instance.getId(), e);
+ }
+ }
+
+ @Override
+ @DataPermission(enable = false)
+ 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);
+
+ // 删除 流程审批部门缓存
+ updateAssigneeDeptRedis(event.getProcessInstanceId());
+
+ // 发送流程实例的状态事件
+ processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
+ BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
+ }
+
+ @Override
+ @DataPermission(enable = false)
+ 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);
+
+ // 删除 流程审批部门缓存
+ updateAssigneeDeptRedis(instance.getProcessInstanceId());
+
+ 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)
+ @DataPermission(enable = false)
+ 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)
+ .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
+ .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
+ .setResult(BpmProcessInstanceResultEnum.REJECT.getResult());
+ processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
+
+ // 删除 流程审批部门缓存
+ updateAssigneeDeptRedis(id);
+
+ // 发送流程被不通过的消息
+ messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason));
+
+ // 发送流程实例的状态事件
+ processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
+ BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
+ }
+
+ @Override
+ public void updateProcessInstanceResult(String id, Integer result) {
+
+ processInstanceExtMapper.update(null, new LambdaUpdateWrapper()
+ .set(BpmProcessInstanceExtDO::getResult, result)
+ .eq(BpmProcessInstanceExtDO::getProcessInstanceId, id));
+ }
+
+ /**
+ * 删除 流程审批部门缓存
+ * @param processInstanceId 流程实例id
+ */
+ public void updateAssigneeDeptRedis(String processInstanceId) {
+
+ stringRedisTemplate.delete("assignee_dept_" + processInstanceId);
+ }
+
+ 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);
+ }
+
+ // 设置当前用户
+ FlowableUtils.setAuthenticatedUserId(userId);
+
+ //获取流程发起人User
+ AdminUserRespDTO startUser = adminUserApi.getUser(userId).getCheckedData();
+ //获取流程发起人部门信息
+ DeptRespDTO startDeptInfo = deptApi.getDept(startUser.getDeptId()).getCheckedData();
+ // 获取岗位信息
+ Set postIds = startUser.getPostIds();
+ ArrayList list = new ArrayList<>(postIds);
+
+ if (CollectionUtil.isEmpty(startUser.getPostIds())) {
+ // 当前审批用户未配置岗位
+ // 操作失败,原因:您未配置岗位,请联系管理员操作
+ throw exception(TASK_OPERATE_FAIL_USER_NO_POST);
+ }
+ if( startUser.getDeptId() == null ) {
+ // 当前审批用户未配置部门
+ // 操作失败,原因:您未配置部门,请联系管理员操作
+ throw exception(TASK_OPERATE_FAIL_USER_NO_DEPT);
+ }
+
+ //配置通用流程变量
+ variables.put("post_id", list.get(0).toString()); // 只获配置的首个岗位
+ variables.put("user_id", userId.toString()); // 配置发起人用户id
+ variables.put("dept_id", startUser.getDeptId().toString()); // 配置发起人部门id
+ variables.put("dept_flag", startDeptInfo.getFlag()); // 配置发起人部门flag
+
+ // 创建流程实例
+ ProcessInstance instance = runtimeService.createProcessInstanceBuilder()
+ .processDefinitionId(definition.getId())
+ .businessKey(businessKey)
+ .name(definition.getName().trim())
+ .variables(variables)
+ .start();
+
+ // 设置流程名字
+ runtimeService.setProcessInstanceName(instance.getId(), definition.getName());
+
+ // 设置流程审批部门缓存
+ stringRedisTemplate.opsForValue().set("assignee_dept_" + instance.getId(), startUser.getDeptId().toString());
+
+ // 延迟处理抄送人信息,避免与事件监听器的并发问题
+ processInstanceCCDeferred(instance, variables);
+
+ return instance.getId();
+ }
+
+ /**
+ * 延迟处理抄送人信息,避免与事件监听器的并发问题
+ */
+ private void processInstanceCCDeferred(ProcessInstance instance, Map variables) {
+ // 使用异步处理,确保主流程不被阻塞
+ CompletableFuture.runAsync(() -> {
+ try {
+ Thread.sleep(100); // 短暂延迟,让事件监听器先执行
+ processCCToUsers(instance, variables);
+ } catch (Exception e) {
+ log.error("异步处理抄送人信息失败,processInstanceId: {}", instance.getProcessInstanceId(), e);
+ }
+ });
+ }
+
+ 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);
+
+ }
+
+ @Override
+ public List getProcessInstancesGroupByModelName(BpmProcessInstanceStatisticsReqVO pageReqVO) {
+ pageReqVO = getUserids(pageReqVO);
+ return processInstanceExtMapper.getProcessInstancesGroupByModelName(pageReqVO);
+ }
+
+ @Override
+ public List getProcessInstancesGroupByResultStatus(BpmProcessInstanceStatisticsReqVO pageReqVO) {
+ pageReqVO = getUserids(pageReqVO);
+ return processInstanceExtMapper.getProcessInstancesGroupByResultStatus(pageReqVO);
+ }
+
+ @Override
+ public PageResult getStatisticsProcessInstancePage(@Valid BpmProcessInstanceMyPageReqVO pageReqVO) {
+ // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
+ PageResult pageResult = processInstanceExtMapper.selectStatisticePage(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());
+
+ Iterator iterator = ids.iterator();
+ while (iterator.hasNext()) {
+ String id = iterator.next();
+ if (id == null) {
+ iterator.remove();
+ }
+ }
+
+ 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
+ * @param
+ * @return
+ */
+ 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);
+
+ String BpmConstantsName = BpmConstants.CC_NAME;
+ // 获取发起人信息
+ AdminUserRespDTO userRespDTO = adminUserApi.getUser(Long.valueOf(instance.getStartUserId())).getCheckedData();
+ DeptRespDTO deptRespDTO = deptApi.getDept(userRespDTO.getDeptId()).getCheckedData();
+
+ //发起人是深圳分公司
+ if (deptRespDTO.getFlag().contains("136")) {
+
+ BpmConstantsName = BpmConstants.CCSZ_NAME;
+ }
+
+ for (BpmTaskAssignRuleDO rule : rules) {
+ String key = rule.getTaskDefinitionKey(); //任务名称
+ Integer type = rule.getType();
+ if (!key.isEmpty() && key.equals(BpmConstantsName) && 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("]");
+ }
+ }
+
+ //流程是采购计划时
+ if (definition.getKey().equals(BpmOAProcureServiceImpl.PROCESS_KEY)) {
+
+ //发起人部门为 生产部及以下时
+ if (deptRespDTO.getFlag().contains("130")) {
+
+ //添加 供应部抄送
+ BpmUserGroupDO userGroup = userGroupService.getUserGroup(121L);
+ 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;
+ }
+ }
+ }
+
+ /**
+ * 创建流程后,添加抄送人
+ * @param instance 流程实例
+ */
+ private void processCCToUsers(ProcessInstance instance, Map variables) {
+
+ // 根据流程名称、流程发起人 查询流程配置的抄送人信息
+ List processCcList = processCcService.getCCListByName(instance.getName(), Long.valueOf(instance.getStartUserId()));
+ // 提取抄送信息用中对应的用户组编号
+ Set userGroupIds = processCcList.stream()
+ .flatMap(data -> data.getUserGroupId().stream())
+ .collect(Collectors.toSet());
+
+ //根据processDefinitionId 将ccIds保存到bpm_process_instance_ext中的ccIds字段
+ BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
+ .setProcessDefinitionId(instance.getProcessDefinitionId())
+ .setFormVariables(variables)
+ .setProcessInstanceId(instance.getProcessInstanceId());
+ if (CollectionUtil.isNotEmpty(userGroupIds)) {
+
+ // 获取用户组信息
+ List userGroups = userGroupService.getUserGroupList(userGroupIds);
+ // 提取用户组中对应的用户ID
+ Set userIds = userGroups.stream()
+ .flatMap(data -> data.getMemberUserIds().stream())
+ .collect(Collectors.toSet());
+
+ // 将用户ID列表转换为字符串
+ String ccIds = userIds.stream()
+ .map(id -> "[" + id + "]") // 每个值用方括号包裹
+ .collect(Collectors.joining());
+
+ //根据processDefinitionId 将ccIds保存到bpm_process_instance_ext中的ccIds字段
+ instanceExtDO.setCcids(ccIds);
+ }
+
+ // 增加幂等性处理,确保更新操作能正常命中
+ updateProcessInstanceExtWithRetry(instanceExtDO);
+ }
+
+ /**
+ * 带重试机制的更新操作,确保幂等性
+ */
+ private void updateProcessInstanceExtWithRetry(BpmProcessInstanceExtDO instanceExtDO) {
+ try {
+ // 先检查记录是否存在
+ BpmProcessInstanceExtDO existingRecord = processInstanceExtMapper.selectByProcessInstanceId(instanceExtDO.getProcessInstanceId());
+ if (existingRecord != null) {
+ // 记录存在,直接更新
+ processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
+ log.debug("异步处理抄送人信息更新成功,processInstanceId: {}", instanceExtDO.getProcessInstanceId());
+ return;
+ }
+
+ // 记录不存在,等待一下再重试
+ Thread.sleep(50);
+ existingRecord = processInstanceExtMapper.selectByProcessInstanceId(instanceExtDO.getProcessInstanceId());
+ if (existingRecord != null) {
+ // 记录存在,执行更新
+ processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
+ log.debug("异步处理抄送人信息重试更新成功,processInstanceId: {}", instanceExtDO.getProcessInstanceId());
+ } else {
+ log.warn("异步处理抄送人信息更新失败,记录不存在,processInstanceId: {}", instanceExtDO.getProcessInstanceId());
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ log.error("异步处理抄送人信息被中断,processInstanceId: {}", instanceExtDO.getProcessInstanceId(), e);
+ } catch (Exception e) {
+ log.error("异步处理抄送人信息失败,processInstanceId: {}", instanceExtDO.getProcessInstanceId(), e);
+ }
+ }
+
+ @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;
+ }
+
+ @Override
+ @DataPermission(enable = false)
+ public BpmProcessInstanceExtDO getProcessInstanceDO(String id) {
+ return processInstanceExtMapper.selectByProcessInstanceId(id);
+ }
+
+ @Resource
+ private FileApi fileApi;
+
+ @Resource
+ @Lazy // 解决循环依赖
+ private BpmOAReimbursementService reimbursementService;
+
+ @Resource
+ @Lazy // 解决循环依赖
+ private BpmOACashService cashService;
+
+ @Resource
+ @Lazy // 解决循环依赖
+ private BpmOAImprestService imprestService;
+
+ @Resource
+ @Lazy // 解决循环依赖
+ private BpmOAPaymentService paymentService;
+
+ @Resource
+ @Lazy
+ private BpmOASalaryService salaryService;
+
+ @Resource
+ private FactoryInfoApi factoryInfoApi;
+
+ @Override
+ public BpmProcessInstancePrintDataRespVO getOAReportPrintData(BpmProcessInstancePrintDataReqVO reqVO) {
+
+ BpmProcessInstancePrintDataRespVO bpmProcessInstancePrintDataRespVO = new BpmProcessInstancePrintDataRespVO();
+ String key = reqVO.getKey(); //流程标识
+ String processInstanceId = reqVO.getId(); //流程实例ID
+ bpmProcessInstancePrintDataRespVO.setId(processInstanceId);
+ bpmProcessInstancePrintDataRespVO.setKey(key);
+
+ BpmProcessInstanceRespVO bpmProcessInstance = getProcessInstanceVO(processInstanceId);
+ Long businessKey = Long.valueOf(bpmProcessInstance.getBusinessKey()); //流程业务表-主键ID
+
+ // 判断是否可见外勤人员信息
+ List userIds = new ArrayList<>();
+ String type = configApi.getConfigKey("sys.user.type").getCheckedData();
+ if ("1".equals(type)) { //不可见时
+
+ // 获取所有外勤人员 用户编号
+ userIds = adminUserApi.getUserIdsByUserNature(4).getCheckedData();
+ }
+
+ // 获得流程审批信息
+ List taskRespVOList = taskService.getTaskListByProcessInstanceId(processInstanceId);
+
+ // 移除外勤人员信息
+ List finalUserIds = userIds;
+ taskRespVOList.removeIf(data -> finalUserIds.contains(data.getAssigneeUser().getId()));
+ //给User添加签名地址
+ taskRespVOList.forEach(taskRespVO ->
+ taskRespVO.getAssigneeUser().setSignURL(
+ fileApi.getUserSignImgPath(
+ taskRespVO.getAssigneeUser().getId()
+ ).getData()
+ )
+ );
+
+ //获取流程抄送用户编号
+ BpmProcessInstanceExtDO processInstanceExtDO = getProcessInstanceDO(processInstanceId);
+
+ //获取流程抄送用户信息
+ List ccUserIds = new ArrayList<>();
+ if (processInstanceExtDO.getCcids() != null && !processInstanceExtDO.getCcids().isEmpty()) {
+
+ Pattern pattern = Pattern.compile("\\[(\\d+)]");
+ Matcher matcher = pattern.matcher(processInstanceExtDO.getCcids());
+ while (matcher.find()) {
+
+ ccUserIds.add(Long.parseLong(matcher.group(1)));
+ }
+
+ // 移除 外勤人员信息
+ ccUserIds.removeAll(userIds);
+ }
+
+ List userRespDTOS = adminUserApi.getUserList(ccUserIds).getCheckedData();
+
+ //获取抄送用户部门信息
+ Map deptMapDTO = deptApi.getDeptMap(convertSet(userRespDTOS, AdminUserRespDTO::getDeptId));
+
+ // 设置抄送人信息
+ List ccUsers = userRespDTOS.stream().map(user -> {
+ BpmOAPrintDataRespVO.CCUser cc = new BpmOAPrintDataRespVO.CCUser();
+ cc.setCcUserId(user.getId());
+ cc.setCcUserName(user.getNickname());
+ cc.setCcDeptName(deptMapDTO.get(user.getDeptId()).getName());
+ return cc;
+ }).collect(Collectors.toList());
+
+ BpmOAPrintDataRespVO printData = new BpmOAPrintDataRespVO();
+ // 设置抄送人
+ printData.setCcUsers(ccUsers);
+ // 设置发起人信息
+ printData.setStartUser(BeanUtils.toBean(bpmProcessInstance.getStartUser(), BpmOAPrintDataRespVO.User.class));
+ switch (key) {
+ case "oa_reimbursement":
+ BpmOAReimbursementDO reimbursement = reimbursementService.getReimbursement(businessKey);
+ BpmOAReimbursementRespVO bpmOAReimbursementRespVO = reimbursementService.convert(reimbursement); //报销业务数据
+
+ // 移除自己得审批节点
+ taskRespVOList.removeIf(data -> data.getAssigneeUser().getId().equals(reimbursement.getUserId()));
+
+ //备用金信息查询
+ BpmOAImprestDO bpmOAImprestDO = imprestService.getImprest(reimbursement.getImprestId());
+ if (bpmOAImprestDO != null) {
+
+ //设置备用金 金额
+ bpmOAReimbursementRespVO.setAmount(bpmOAImprestDO.getAmount());
+ // 设置备用金 剩余金额
+ bpmOAReimbursementRespVO.setRemainingAmount(bpmOAImprestDO.getAmount().subtract(bpmOAImprestDO.getReimbursedAmount()));
+ }
+
+
+ printData.setBpmOAReimbursementRespVO(bpmOAReimbursementRespVO);
+ printData.setProcessTasks(taskRespVOList);
+ break;
+ case "oa_cash":
+ BpmOACashDO cashDO = cashService.getCash(businessKey);
+ BpmOACashRespVO cashRespVO = cashService.convertCash(cashDO);
+
+ // 移除自己得审批节点
+ taskRespVOList.removeIf(data -> data.getAssigneeUser().getId().equals(cashDO.getUserId()));
+
+ //备用金信息查询
+ BpmOAImprestDO cashImprestDO = imprestService.getImprest(cashDO.getImprestId());
+ if (cashImprestDO != null) {
+
+ //设置备用金 金额
+ cashRespVO.setAmount(cashImprestDO.getAmount());
+ // 设置备用金 剩余金额
+ cashRespVO.setRemainingAmount(cashImprestDO.getAmount().subtract(cashImprestDO.getReimbursedAmount()));
+ }
+
+ printData.setBpmOACashRespVO(cashRespVO);
+ printData.setProcessTasks(taskRespVOList);
+ break;
+ case "oa_payment_2":
+ BpmOAPaymentDO paymentDO = paymentService.getPayment(businessKey);
+ BpmOAPaymentRespVO paymentRespVO = paymentService.convertPayment(paymentDO);
+
+ // 移除自己得审批节点
+ taskRespVOList.removeIf(data -> data.getAssigneeUser().getId().equals(paymentDO.getUserId()));
+
+ printData.setBpmOAPaymentRespVO(paymentRespVO);
+ printData.setProcessTasks(taskRespVOList);
+ break;
+ case "oa_salary_2":
+ BpmOASalaryDO salaryDO = salaryService.getSalary(businessKey);
+ BpmOASalaryRespVO salaryRespVO = BeanUtils.toBean(salaryDO, BpmOASalaryRespVO.class);
+
+ if (salaryDO.getCompanyDeptId() != null) {
+ // 获取部门详情
+ DeptRespDTO dto = deptApi.getDept(salaryDO.getCompanyDeptId()).getCheckedData();
+
+ salaryRespVO.setCompanyName(dto.getName());
+ }else {
+ // 获取工厂信息
+ Set factoryIds = salaryDO.getFactoryDeptId();
+ List factoryInfoDTOS = factoryInfoApi.getFactoryInfoList(factoryIds).getCheckedData();
+
+ String factoryNames = factoryInfoDTOS.stream()
+ .map(FactoryInfoDTO::getShortName)
+ .filter(Objects::nonNull)
+ .collect(Collectors.joining(","));
+
+ salaryRespVO.setCompanyName(factoryNames);
+ }
+
+ // 移除自己得审批节点
+ taskRespVOList.removeIf(data -> data.getAssigneeUser().getId().equals(salaryDO.getUserId()));
+
+ printData.setBpmOASalaryRespVO(salaryRespVO);
+ printData.setProcessTasks(taskRespVOList);
+ break;
+ }
+ bpmProcessInstancePrintDataRespVO.setPrintDataRespVO(printData);
+ return bpmProcessInstancePrintDataRespVO;
+ }
+
+ @Override
+ public Map> getProcessInstanceResultStatusStatisticsGroupTime(BpmProcessInstanceStatisticsReqVO pageReqVO) {
+ pageReqVO = getUserids(pageReqVO);
+ pageReqVO.setRecentDays(pageReqVO.getRecentDays() == null ? 7 : pageReqVO.getRecentDays());
+ List list = processInstanceExtMapper.getProcessInstanceResultStatusStatisticsGroupTime(pageReqVO);
+ // -- 获取到日期列表
+ LocalDateTime now = LocalDateTimeUtil.now();
+ LocalDateTime offset = LocalDateTimeUtil.offset(LocalDateTimeUtil.now(), -1L * pageReqVO.getRecentDays(), ChronoUnit.DAYS);
+ List dateList = DateUtils.betweenDayList(offset, now);
+
+
+ //根据时间分组
+ Map> map = list.stream().collect(Collectors.groupingBy(BpmProcessInstanceResultStatusStatisticsGroupTimeVO::getTime));
+
+ List keys = new ArrayList<>(CollectionUtil.subtract(dateList, new ArrayList<>(map.keySet())));
+ if (CollectionUtil.isNotEmpty(keys)) {
+ for (String key : keys) {
+ map.put(key, new ArrayList<>());
+ }
+ }
+
+ List statusList = Arrays.asList(1, 2, 3, 4);
+ for (Map.Entry> entry : map.entrySet()) {
+ List items = entry.getValue();
+ List results = items.stream().map(BpmProcessInstanceResultStatusStatisticsGroupTimeVO::getResult).collect(Collectors.toList());
+ List saveList = new ArrayList<>(CollectionUtil.subtract(statusList, results));
+ // -- 如果没有的话组装上缺少的
+ if (CollectionUtil.isNotEmpty(saveList)) {
+ for (Integer status : saveList) {
+ items.add(new BpmProcessInstanceResultStatusStatisticsGroupTimeVO()
+ .setTime(entry.getKey())
+ .setTotalCount(0)
+ .setResult(status)
+ .setName(""));
+ }
+ }
+ }
+ return map;
+ }
+}