mirror of
https://gitee.com/orangeform/orange-admin.git
synced 2026-01-18 02:56:30 +08:00
commit:同步2.2版本
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
package com.orangeforms.common.flow.base.service;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.orangeforms.common.core.base.service.BaseService;
|
||||
import com.orangeforms.common.core.object.MyRelationParam;
|
||||
import com.orangeforms.common.core.util.MyDateUtil;
|
||||
import com.orangeforms.common.flow.constant.FlowApprovalType;
|
||||
import com.orangeforms.common.flow.constant.FlowTaskStatus;
|
||||
import com.orangeforms.common.flow.model.FlowTaskComment;
|
||||
@@ -50,13 +53,44 @@ public abstract class BaseFlowService<M, K extends Serializable> extends BaseSer
|
||||
flowApiService.completeTask(task, comment, variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否支持业务数据同步。每个子类需要根据实际情况判断是否需要支持。
|
||||
*
|
||||
* @return true支持,否则false。
|
||||
*/
|
||||
public boolean supportSyncBusinessData() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在流程实例审批结束后,需要进行审批表到发布表数据同步的服务实现子类,需要实现该方法。
|
||||
*
|
||||
* @param processInstanceId 流程实例Id。
|
||||
* @param businessKey 业务主键Id。如果与实际主键值类型不同,需要在子类中自行完成类型转换。
|
||||
*/
|
||||
public void doSyncBusinessData(String processInstanceId, String businessKey) {
|
||||
public void syncBusinessData(String processInstanceId, String businessKey) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取业务详情数据。
|
||||
*
|
||||
* @param processInstanceId 流程实例Id。
|
||||
* @param businessKey 业务主键Id。如果与实际主键值类型不同,需要在子类中自行完成类型转换。
|
||||
* @return 业务主表数据,以及关联从表数据。
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public String getBusinessData(String processInstanceId, String businessKey) {
|
||||
M data;
|
||||
if (idFieldClass.equals(Long.class)) {
|
||||
Long dataId = Long.valueOf(businessKey);
|
||||
data = this.getByIdWithRelation((K) dataId, MyRelationParam.full());
|
||||
} else if (idFieldClass.equals(Integer.class)) {
|
||||
Integer dataId = Integer.valueOf(businessKey);
|
||||
data = this.getByIdWithRelation((K) dataId, MyRelationParam.full());
|
||||
} else {
|
||||
data = this.getByIdWithRelation((K) businessKey, MyRelationParam.full());
|
||||
}
|
||||
return JSON.toJSONStringWithDateFormat(data, MyDateUtil.COMMON_SHORT_DATETIME_FORMAT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,11 @@ public class FlowConstant {
|
||||
*/
|
||||
public final static String PROC_INSTANCE_START_USER_NAME_VAR = "startUserName";
|
||||
|
||||
/**
|
||||
* 流程任务的指定人变量。
|
||||
*/
|
||||
public final static String TASK_APPOINTED_ASSIGNEE_VAR = "appointedAssignee";
|
||||
|
||||
/**
|
||||
* 操作类型变量。
|
||||
*/
|
||||
@@ -64,47 +69,62 @@ public class FlowConstant {
|
||||
public final static String MULTI_ASSIGNEE_LIST_VAR = "assigneeList";
|
||||
|
||||
/**
|
||||
* 上级部门领导审批变量
|
||||
* 上级部门领导审批变量。
|
||||
*/
|
||||
public final static String GROUP_TYPE_UP_DEPT_POST_LEADER_VAR = "upDeptPostLeader";
|
||||
|
||||
/**
|
||||
* 上级部门领导审批变量
|
||||
* 本部门领导审批变量。
|
||||
*/
|
||||
public final static String GROUP_TYPE_DEPT_POST_LEADER_VAR = "deptPostLeader";
|
||||
|
||||
/**
|
||||
* 所有部门岗位审批变量
|
||||
* 所有部门岗位审批变量。
|
||||
*/
|
||||
public final static String GROUP_TYPE_ALL_DEPT_POST_VAR = "allDeptPost";
|
||||
|
||||
/**
|
||||
* 本部门岗位审批变量
|
||||
* 本部门岗位审批变量。
|
||||
*/
|
||||
public final static String GROUP_TYPE_SELF_DEPT_POST_VAR = "selfDeptPost";
|
||||
|
||||
/**
|
||||
* 上级部门岗位审批变量
|
||||
* 上级部门岗位审批变量。
|
||||
*/
|
||||
public final static String GROUP_TYPE_UP_DEPT_POST_VAR = "upDeptPost";
|
||||
|
||||
/**
|
||||
* 任意部门关联的岗位审批变量
|
||||
* 任意部门关联的岗位审批变量。
|
||||
*/
|
||||
public final static String GROUP_TYPE_DEPT_POST_VAR = "deptPost";
|
||||
|
||||
/**
|
||||
* 岗位
|
||||
* 指定角色分组变量。
|
||||
*/
|
||||
public final static String GROUP_TYPE_ROLE_VAR = "role";
|
||||
|
||||
/**
|
||||
* 指定部门分组变量。
|
||||
*/
|
||||
public final static String GROUP_TYPE_DEPT_VAR = "dept";
|
||||
|
||||
/**
|
||||
* 指定用户分组变量。
|
||||
*/
|
||||
public final static String GROUP_TYPE_USER_VAR = "user";
|
||||
|
||||
/**
|
||||
* 岗位。
|
||||
*/
|
||||
public final static String GROUP_TYPE_POST = "POST";
|
||||
|
||||
/**
|
||||
* 上级部门领导审批
|
||||
* 上级部门领导审批。
|
||||
*/
|
||||
public final static String GROUP_TYPE_UP_DEPT_POST_LEADER = "UP_DEPT_POST_LEADER";
|
||||
|
||||
/**
|
||||
* 本部门岗位领导审批
|
||||
* 本部门岗位领导审批。
|
||||
*/
|
||||
public final static String GROUP_TYPE_DEPT_POST_LEADER = "DEPT_POST_LEADER";
|
||||
|
||||
@@ -114,7 +134,17 @@ public class FlowConstant {
|
||||
public final static String SELF_DEPT_POST_PREFIX = "SELF_DEPT_";
|
||||
|
||||
/**
|
||||
* 本部门岗位前缀。
|
||||
* 上级部门岗位前缀。
|
||||
*/
|
||||
public final static String UP_DEPT_POST_PREFIX = "UP_DEPT_";
|
||||
|
||||
/**
|
||||
* 当前流程实例所有任务的抄送数据前缀。
|
||||
*/
|
||||
public final static String COPY_DATA_MAP_PREFIX = "copyDataMap_";
|
||||
|
||||
/**
|
||||
* 作为临时变量存入任务变量JSONObject对象时的key。
|
||||
*/
|
||||
public static final String COPY_DATA_KEY = "copyDataKey";
|
||||
}
|
||||
|
||||
@@ -18,8 +18,10 @@ import com.orangeforms.common.core.util.MyCommonUtil;
|
||||
import com.orangeforms.common.core.util.MyModelUtil;
|
||||
import com.orangeforms.common.core.util.MyPageUtil;
|
||||
import com.orangeforms.common.core.validator.UpdateGroup;
|
||||
import com.orangeforms.common.core.exception.MyRuntimeException;
|
||||
import com.orangeforms.common.flow.object.FlowTaskMultiSignAssign;
|
||||
import com.orangeforms.common.flow.constant.FlowTaskType;
|
||||
import com.orangeforms.common.flow.constant.FlowConstant;
|
||||
import com.orangeforms.common.flow.dto.*;
|
||||
import com.orangeforms.common.flow.model.*;
|
||||
import com.orangeforms.common.flow.model.constant.FlowEntryStatus;
|
||||
@@ -371,6 +373,10 @@ public class FlowEntryController {
|
||||
if (deptPostList != null) {
|
||||
flowTaskExt.setDeptPostListJson(JSON.toJSONString(deptPostList));
|
||||
}
|
||||
List<JSONObject> copyList = this.buildCopyListExtensionElement(extensionMap);
|
||||
if (copyList != null) {
|
||||
flowTaskExt.setCopyListJson(JSON.toJSONString(copyList));
|
||||
}
|
||||
JSONObject candidateGroupObject = this.buildUserCandidateGroupsExtensionElement(extensionMap);
|
||||
if (candidateGroupObject != null) {
|
||||
String type = candidateGroupObject.getString("type");
|
||||
@@ -572,6 +578,37 @@ public class FlowEntryController {
|
||||
return resultList;
|
||||
}
|
||||
|
||||
private List<JSONObject> buildCopyListExtensionElement(Map<String, List<ExtensionElement>> extensionMap) {
|
||||
List<ExtensionElement> copyElements =
|
||||
this.getMyExtensionElementList(extensionMap, "copyItemList", "copyItem");
|
||||
if (CollUtil.isEmpty(copyElements)) {
|
||||
return null;
|
||||
}
|
||||
List<JSONObject> resultList = new LinkedList<>();
|
||||
for (ExtensionElement e : copyElements) {
|
||||
JSONObject copyJsonData = new JSONObject();
|
||||
String type = e.getAttributeValue(null, "type");
|
||||
copyJsonData.put("type", type);
|
||||
if (!StrUtil.equalsAny(type, FlowConstant.GROUP_TYPE_DEPT_POST_LEADER_VAR,
|
||||
FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER_VAR,
|
||||
FlowConstant.GROUP_TYPE_USER_VAR,
|
||||
FlowConstant.GROUP_TYPE_ROLE_VAR,
|
||||
FlowConstant.GROUP_TYPE_DEPT_VAR,
|
||||
FlowConstant.GROUP_TYPE_DEPT_POST_VAR,
|
||||
FlowConstant.GROUP_TYPE_ALL_DEPT_POST_VAR,
|
||||
FlowConstant.GROUP_TYPE_SELF_DEPT_POST_VAR,
|
||||
FlowConstant.GROUP_TYPE_UP_DEPT_POST_VAR)) {
|
||||
throw new MyRuntimeException("Invalid TYPE [" + type + " ] for CopyItenList Extension!");
|
||||
}
|
||||
String id = e.getAttributeValue(null, "id");
|
||||
if (StrUtil.isNotBlank(id)) {
|
||||
copyJsonData.put("id", id);
|
||||
}
|
||||
resultList.add(copyJsonData);
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
private List<ExtensionElement> getMyExtensionElementList(
|
||||
Map<String, List<ExtensionElement>> extensionMap, String rootName, String childName) {
|
||||
List<ExtensionElement> elementList = extensionMap.get(rootName);
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package com.orangeforms.common.flow.controller;
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.orangeforms.common.core.annotation.MyRequestBody;
|
||||
import com.orangeforms.common.core.object.*;
|
||||
import com.orangeforms.common.core.constant.ErrorCodeEnum;
|
||||
import com.orangeforms.common.core.util.MyPageUtil;
|
||||
import com.orangeforms.common.flow.model.constant.FlowMessageType;
|
||||
import com.orangeforms.common.flow.model.FlowMessage;
|
||||
import com.orangeforms.common.flow.service.FlowMessageService;
|
||||
import com.orangeforms.common.flow.vo.FlowMessageVo;
|
||||
@@ -29,9 +32,24 @@ public class FlowMessageController {
|
||||
@Autowired
|
||||
private FlowMessageService flowMessageService;
|
||||
|
||||
/**
|
||||
* 获取当前用户的未读消息总数。
|
||||
* NOTE:白名单接口。
|
||||
*
|
||||
* @return 应答结果对象,包含当前用户的未读消息总数。
|
||||
*/
|
||||
@GetMapping("/getMessageCount")
|
||||
public ResponseResult<JSONObject> getMessageCount() {
|
||||
JSONObject resultData = new JSONObject();
|
||||
resultData.put("remindingMessageCount", flowMessageService.countRemindingMessageListByUser());
|
||||
resultData.put("copyMessageCount", flowMessageService.countCopyMessageByUser());
|
||||
return ResponseResult.success(resultData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户的催办消息列表。
|
||||
* 不仅仅包含,其中包括当前用户所属角色、部门和岗位的候选组催办消息。
|
||||
* NOTE:白名单接口。
|
||||
*
|
||||
* @return 应答结果对象,包含查询结果集。
|
||||
*/
|
||||
@@ -43,4 +61,48 @@ public class FlowMessageController {
|
||||
List<FlowMessage> flowMessageList = flowMessageService.getRemindingMessageListByUser();
|
||||
return ResponseResult.success(MyPageUtil.makeResponseData(flowMessageList, FlowMessage.INSTANCE));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户的抄送消息列表。
|
||||
* 不仅仅包含,其中包括当前用户所属角色、部门和岗位的候选组抄送消息。
|
||||
* NOTE:白名单接口。
|
||||
*
|
||||
* @param read true表示已读,false表示未读。
|
||||
* @return 应答结果对象,包含查询结果集。
|
||||
*/
|
||||
@PostMapping("/listCopyMessage")
|
||||
public ResponseResult<MyPageData<FlowMessageVo>> listCopyMessage(
|
||||
@MyRequestBody MyPageParam pageParam, @MyRequestBody Boolean read) {
|
||||
if (pageParam != null) {
|
||||
PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize());
|
||||
}
|
||||
List<FlowMessage> flowMessageList = flowMessageService.getCopyMessageListByUser(read);
|
||||
return ResponseResult.success(MyPageUtil.makeResponseData(flowMessageList, FlowMessage.INSTANCE));
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取抄送消息,同时更新当前用户对指定抄送消息的读取状态。
|
||||
*
|
||||
* @param messageId 消息Id。
|
||||
* @return 应答结果对象。
|
||||
*/
|
||||
@PostMapping("/readCopyTask")
|
||||
public ResponseResult<Void> readCopyTask(@MyRequestBody Long messageId) {
|
||||
String errorMessage;
|
||||
// 验证流程任务的合法性。
|
||||
FlowMessage flowMessage = flowMessageService.getById(messageId);
|
||||
if (flowMessage == null) {
|
||||
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
|
||||
}
|
||||
if (flowMessage.getMessageType() != FlowMessageType.COPY_TYPE) {
|
||||
errorMessage = "数据验证失败,当前消息不是抄送类型消息!";
|
||||
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||
}
|
||||
if (!flowMessageService.isCandidateIdentityOnMessage(messageId)) {
|
||||
errorMessage = "数据验证失败,当前用户没有权限访问该消息!";
|
||||
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||
}
|
||||
flowMessageService.readCopyTask(messageId);
|
||||
return ResponseResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.orangeforms.common.flow.controller;
|
||||
import io.swagger.annotations.Api;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
@@ -15,8 +14,10 @@ import com.orangeforms.common.core.util.MyPageUtil;
|
||||
import com.orangeforms.common.flow.constant.FlowApprovalType;
|
||||
import com.orangeforms.common.flow.constant.FlowConstant;
|
||||
import com.orangeforms.common.flow.constant.FlowTaskStatus;
|
||||
import com.orangeforms.common.flow.model.constant.FlowMessageType;
|
||||
import com.orangeforms.common.flow.model.*;
|
||||
import com.orangeforms.common.flow.service.*;
|
||||
import com.orangeforms.common.flow.util.FlowCustomExtFactory;
|
||||
import com.orangeforms.common.flow.util.FlowOperationHelper;
|
||||
import com.orangeforms.common.flow.vo.FlowTaskCommentVo;
|
||||
import com.orangeforms.common.flow.vo.FlowTaskVo;
|
||||
@@ -29,6 +30,7 @@ import org.flowable.bpmn.model.Process;
|
||||
import org.flowable.bpmn.model.SequenceFlow;
|
||||
import org.flowable.engine.history.HistoricActivityInstance;
|
||||
import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.flowable.task.api.history.HistoricTaskInstance;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -69,6 +71,8 @@ public class FlowOperationController {
|
||||
private FlowMessageService flowMessageService;
|
||||
@Autowired
|
||||
private FlowOperationHelper flowOperationHelper;
|
||||
@Autowired
|
||||
private FlowCustomExtFactory flowCustomExtFactory;
|
||||
|
||||
/**
|
||||
* 根据指定流程的主版本,发起一个流程实例。
|
||||
@@ -199,10 +203,6 @@ public class FlowOperationController {
|
||||
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
|
||||
}
|
||||
FlowTaskComment taskComment = taskCommentList.get(0);
|
||||
if (ObjectUtil.notEqual(taskComment.getCreateUserId(), TokenData.takeFromRequest().getUserId())) {
|
||||
errorMessage = "数据验证失败,当前流程发起人与当前用户不匹配!";
|
||||
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||
}
|
||||
HistoricTaskInstance task = flowApiService.getHistoricTaskInstance(processInstanceId, taskComment.getTaskId());
|
||||
if (StrUtil.isBlank(task.getFormKey())) {
|
||||
errorMessage = "数据验证失败,指定任务的formKey属性不存在,请重新修改流程图!";
|
||||
@@ -213,6 +213,59 @@ public class FlowOperationController {
|
||||
return ResponseResult.success(taskInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据消息Id,获取流程Id关联的业务数据。
|
||||
* NOTE:白名单接口。
|
||||
*
|
||||
* @param messageId 抄送消息Id。
|
||||
* @param snapshot 是否获取抄送或传阅时任务的业务快照数据。如果为true,后续任务导致的业务数据修改,将不会返回给前端。
|
||||
* @return 抄送消息关联的流程实例业务数据。
|
||||
*/
|
||||
@DisableDataFilter
|
||||
@GetMapping("/viewCopyBusinessData")
|
||||
public ResponseResult<JSONObject> viewCopyBusinessData(
|
||||
@RequestParam Long messageId, @RequestParam(required = false) Boolean snapshot) {
|
||||
String errorMessage;
|
||||
// 验证流程任务的合法性。
|
||||
FlowMessage flowMessage = flowMessageService.getById(messageId);
|
||||
if (flowMessage == null) {
|
||||
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
|
||||
}
|
||||
if (flowMessage.getMessageType() != FlowMessageType.COPY_TYPE) {
|
||||
errorMessage = "数据验证失败,当前消息不是抄送类型消息!";
|
||||
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||
}
|
||||
if (flowMessage.getOnlineFormData() == null || flowMessage.getOnlineFormData()) {
|
||||
errorMessage = "数据验证失败,当前消息为在线表单数据,不能通过该接口获取!";
|
||||
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||
}
|
||||
if (!flowMessageService.isCandidateIdentityOnMessage(messageId)) {
|
||||
errorMessage = "数据验证失败,当前用户没有权限访问该消息!";
|
||||
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||
}
|
||||
JSONObject businessObject = null;
|
||||
if (snapshot != null && snapshot) {
|
||||
if (StrUtil.isNotBlank(flowMessage.getBusinessDataShot())) {
|
||||
businessObject = JSON.parseObject(flowMessage.getBusinessDataShot());
|
||||
}
|
||||
return ResponseResult.success(businessObject);
|
||||
}
|
||||
ProcessInstance instance = flowApiService.getProcessInstance(flowMessage.getProcessInstanceId());
|
||||
// 如果业务主数据为空,则直接返回。
|
||||
if (StrUtil.isBlank(instance.getBusinessKey())) {
|
||||
errorMessage = "数据验证失败,当前消息为所属流程实例没有包含业务主键Id!";
|
||||
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||
}
|
||||
String businessData = flowCustomExtFactory.getBusinessDataExtHelper().getBusinessData(
|
||||
flowMessage.getProcessDefinitionKey(), flowMessage.getProcessInstanceId(), instance.getBusinessKey());
|
||||
if (StrUtil.isNotBlank(businessData)) {
|
||||
businessObject = JSON.parseObject(businessData);
|
||||
}
|
||||
// 将当前消息更新为已读
|
||||
flowMessageService.readCopyTask(messageId);
|
||||
return ResponseResult.success(businessObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交多实例加签。
|
||||
*
|
||||
@@ -292,6 +345,34 @@ public class FlowOperationController {
|
||||
return ResponseResult.success(totalCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 主动驳回当前的待办任务到开始节点,只用当前待办任务的指派人或者候选者才能完成该操作。
|
||||
*
|
||||
* @param processInstanceId 流程实例Id。
|
||||
* @param taskId 待办任务Id。
|
||||
* @param comment 驳回备注。
|
||||
* @return 操作应答结果。
|
||||
*/
|
||||
@PostMapping("/rejectToStartUserTask")
|
||||
public ResponseResult<Void> rejectToStartUserTask(
|
||||
@MyRequestBody(required = true) String processInstanceId,
|
||||
@MyRequestBody(required = true) String taskId,
|
||||
@MyRequestBody(required = true) String comment) {
|
||||
String errorMessage;
|
||||
ResponseResult<Task> taskResult =
|
||||
flowOperationHelper.verifySubmitAndGetTask(processInstanceId, taskId, null);
|
||||
if (!taskResult.isSuccess()) {
|
||||
return ResponseResult.errorFrom(taskResult);
|
||||
}
|
||||
FlowTaskComment firstTaskComment = flowTaskCommentService.getFirstFlowTaskComment(processInstanceId);
|
||||
CallResult result = flowApiService.backToRuntimeTask(
|
||||
taskResult.getData(), firstTaskComment.getTaskKey(), true, comment);
|
||||
if (!result.isSuccess()) {
|
||||
return ResponseResult.errorFrom(result);
|
||||
}
|
||||
return ResponseResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 主动驳回当前的待办任务,只用当前待办任务的指派人或者候选者才能完成该操作。
|
||||
*
|
||||
@@ -311,7 +392,7 @@ public class FlowOperationController {
|
||||
if (!taskResult.isSuccess()) {
|
||||
return ResponseResult.errorFrom(taskResult);
|
||||
}
|
||||
CallResult result = flowApiService.backToLastRuntimeTask(taskResult.getData(), true, comment);
|
||||
CallResult result = flowApiService.backToRuntimeTask(taskResult.getData(), null, true, comment);
|
||||
if (!result.isSuccess()) {
|
||||
return ResponseResult.errorFrom(result);
|
||||
}
|
||||
@@ -368,8 +449,8 @@ public class FlowOperationController {
|
||||
Task task = activeTaskList.get(0);
|
||||
task.setAssignee(TokenData.takeFromRequest().getLoginName());
|
||||
} else {
|
||||
CallResult result = flowApiService
|
||||
.backToLastRuntimeTask(activeTaskList.get(0), false, comment);
|
||||
CallResult result =
|
||||
flowApiService.backToRuntimeTask(activeTaskList.get(0), null, false, comment);
|
||||
if (!result.isSuccess()) {
|
||||
return ResponseResult.errorFrom(result);
|
||||
}
|
||||
@@ -432,14 +513,17 @@ public class FlowOperationController {
|
||||
//获取流程实例的历史节点(全部执行过的节点,被拒绝的任务节点将会出现多次)
|
||||
List<HistoricActivityInstance> activityInstanceList =
|
||||
flowApiService.getHistoricActivityInstanceList(processInstanceId);
|
||||
List<String> activityInstanceTask = activityInstanceList.stream()
|
||||
.filter(s -> !StrUtil.equals(s.getActivityType(), "sequenceFlow"))
|
||||
.map(HistoricActivityInstance::getActivityId).collect(Collectors.toList());
|
||||
Set<String> finishedTaskSequenceSet = new LinkedHashSet<>();
|
||||
for (int i = 0; i < activityInstanceList.size(); i++) {
|
||||
HistoricActivityInstance current = activityInstanceList.get(i);
|
||||
if (i != activityInstanceList.size() - 1) {
|
||||
HistoricActivityInstance next = activityInstanceList.get(i + 1);
|
||||
finishedTaskSequenceSet.add(current.getActivityId() + next.getActivityId());
|
||||
for (int i = 0; i < activityInstanceTask.size(); i++) {
|
||||
String current = activityInstanceTask.get(i);
|
||||
if (i != activityInstanceTask.size() - 1) {
|
||||
String next = activityInstanceTask.get(i + 1);
|
||||
finishedTaskSequenceSet.add(current + next);
|
||||
}
|
||||
finishedTaskSet.add(current.getActivityId());
|
||||
finishedTaskSet.add(current);
|
||||
}
|
||||
Set<String> finishedSequenceFlowSet = new HashSet<>();
|
||||
finishedTaskSequenceSet.forEach(s -> finishedSequenceFlowSet.add(allSequenceFlowMap.get(s)));
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.orangeforms.common.flow.dao;
|
||||
|
||||
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
|
||||
import com.orangeforms.common.flow.model.FlowMessageCandidateIdentity;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 流程任务消息的候选身份数据操作访问接口。
|
||||
@@ -10,4 +11,11 @@ import com.orangeforms.common.flow.model.FlowMessageCandidateIdentity;
|
||||
* @date 2021-06-06
|
||||
*/
|
||||
public interface FlowMessageCandidateIdentityMapper extends BaseDaoMapper<FlowMessageCandidateIdentity> {
|
||||
|
||||
/**
|
||||
* 删除指定流程实例的消息关联数据。
|
||||
*
|
||||
* @param processInstanceId 流程实例Id。
|
||||
*/
|
||||
void deleteByProcessInstanceId(@Param("processInstanceId") String processInstanceId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.orangeforms.common.flow.dao;
|
||||
|
||||
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
|
||||
import com.orangeforms.common.flow.model.FlowMessageIdentityOperation;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 流程任务消息所属用户的操作数据操作访问接口。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2021-06-06
|
||||
*/
|
||||
public interface FlowMessageIdentityOperationMapper extends BaseDaoMapper<FlowMessageIdentityOperation> {
|
||||
|
||||
/**
|
||||
* 删除指定流程实例的消息关联数据。
|
||||
*
|
||||
* @param processInstanceId 流程实例Id。
|
||||
*/
|
||||
void deleteByProcessInstanceId(@Param("processInstanceId") String processInstanceId);
|
||||
}
|
||||
@@ -24,4 +24,37 @@ public interface FlowMessageMapper extends BaseDaoMapper<FlowMessage> {
|
||||
*/
|
||||
List<FlowMessage> getRemindingMessageListByUser(
|
||||
@Param("loginName") String loginName, @Param("groupIdSet") Set<String> groupIdSet);
|
||||
|
||||
/**
|
||||
* 获取指定用户和身份分组Id集合的抄送消息列表。
|
||||
*
|
||||
* @param loginName 用户登录名。
|
||||
* @param groupIdSet 用户身份分组Id集合。
|
||||
* @param read true表示已读,false表示未读。
|
||||
* @return 查询后的抄送消息列表。
|
||||
*/
|
||||
List<FlowMessage> getCopyMessageListByUser(
|
||||
@Param("loginName") String loginName,
|
||||
@Param("groupIdSet") Set<String> groupIdSet,
|
||||
@Param("read") Boolean read);
|
||||
|
||||
/**
|
||||
* 计算当前用户催办消息的数量。
|
||||
*
|
||||
* @param loginName 用户登录名。
|
||||
* @param groupIdSet 用户身份分组Id集合。
|
||||
* @return 数据数量。
|
||||
*/
|
||||
int countRemindingMessageListByUser(
|
||||
@Param("loginName") String loginName, @Param("groupIdSet") Set<String> groupIdSet);
|
||||
|
||||
/**
|
||||
* 计算当前用户未读抄送消息的数量。
|
||||
*
|
||||
* @param loginName 用户登录名。
|
||||
* @param groupIdSet 用户身份分组Id集合。
|
||||
* @return 数据数量
|
||||
*/
|
||||
int countCopyMessageListByUser(
|
||||
@Param("loginName") String loginName, @Param("groupIdSet") Set<String> groupIdSet);
|
||||
}
|
||||
|
||||
@@ -9,11 +9,14 @@ import java.util.*;
|
||||
|
||||
/**
|
||||
* 工作流工单表数据操作访问接口。
|
||||
* 如果当前系统支持数据权限过滤,当前用户必须要能看自己的工单数据,所以需要把EnableDataPerm
|
||||
* 的mustIncludeUserRule参数设置为true,即便当前用户的数据权限中并不包含DataPermRuleType.TYPE_USER_ONLY,
|
||||
* 数据过滤拦截组件也会自动补偿该类型的数据权限,以便当前用户可以看到自己发起的工单。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2021-06-06
|
||||
*/
|
||||
@EnableDataPerm
|
||||
@EnableDataPerm(mustIncludeUserRule = true)
|
||||
public interface FlowWorkOrderMapper extends BaseDaoMapper<FlowWorkOrder> {
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,8 +12,29 @@
|
||||
<result column="create_user_id" jdbcType="BIGINT" property="createUserId"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 如果有逻辑删除字段过滤,请写到这里 -->
|
||||
<sql id="filterRef">
|
||||
<!-- 这里必须加上全包名,否则当filterRef被其他Mapper.xml包含引用的时候,就会调用Mapper.xml中的该SQL片段 -->
|
||||
<include refid="com.orangeforms.common.flow.dao.FlowCategoryMapper.inputFilterRef"/>
|
||||
</sql>
|
||||
|
||||
<!-- 这里仅包含调用接口输入的主表过滤条件 -->
|
||||
<sql id="inputFilterRef">
|
||||
<if test="flowCategoryFilter != null">
|
||||
<if test="flowCategoryFilter.name != null and flowCategoryFilter.name != ''">
|
||||
AND zz_flow_category.name = #{flowCategoryFilter.name}
|
||||
</if>
|
||||
<if test="flowCategoryFilter.code != null and flowCategoryFilter.code != ''">
|
||||
AND zz_flow_category.code = #{flowCategoryFilter.code}
|
||||
</if>
|
||||
</if>
|
||||
</sql>
|
||||
|
||||
<select id="getFlowCategoryList" resultMap="BaseResultMap" parameterType="com.orangeforms.common.flow.model.FlowCategory">
|
||||
SELECT * FROM zz_flow_category
|
||||
<where>
|
||||
<include refid="filterRef"/>
|
||||
</where>
|
||||
<if test="orderBy != null and orderBy != ''">
|
||||
ORDER BY ${orderBy}
|
||||
</if>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<result column="process_definition_key" jdbcType="VARCHAR" property="processDefinitionKey"/>
|
||||
<result column="category_id" jdbcType="BIGINT" property="categoryId"/>
|
||||
<result column="main_entry_publish_id" jdbcType="BIGINT" property="mainEntryPublishId"/>
|
||||
<result column="lastest_publish_time" jdbcType="TIMESTAMP" property="lastestPublishTime"/>
|
||||
<result column="latest_publish_time" jdbcType="TIMESTAMP" property="latestPublishTime"/>
|
||||
<result column="status" jdbcType="INTEGER" property="status"/>
|
||||
<result column="bpmn_xml" jdbcType="LONGVARCHAR" property="bpmnXml"/>
|
||||
<result column="bind_form_type" jdbcType="INTEGER" property="bindFormType"/>
|
||||
@@ -51,7 +51,7 @@
|
||||
process_definition_key,
|
||||
category_id,
|
||||
main_entry_publish_id,
|
||||
lastest_publish_time,
|
||||
latest_publish_time,
|
||||
status,
|
||||
bind_form_type,
|
||||
page_id,
|
||||
|
||||
@@ -7,4 +7,10 @@
|
||||
<result column="candidate_type" jdbcType="VARCHAR" property="candidateType"/>
|
||||
<result column="candidate_id" jdbcType="VARCHAR" property="candidateId"/>
|
||||
</resultMap>
|
||||
|
||||
<delete id="deleteByProcessInstanceId">
|
||||
DELETE FROM zz_flow_message_candicate_identity a
|
||||
WHERE EXISTS (SELECT * FROM zz_flow_message b
|
||||
WHERE a.message_id = b.message_id AND b.process_instance_id = #{processInstanceId})
|
||||
</delete>
|
||||
</mapper>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.orangeforms.common.flow.dao.FlowMessageIdentityOperationMapper">
|
||||
<resultMap id="BaseResultMap" type="com.orangeforms.common.flow.model.FlowMessageIdentityOperation">
|
||||
<id column="id" jdbcType="BIGINT" property="id"/>
|
||||
<result column="message_id" jdbcType="BIGINT" property="messageId"/>
|
||||
<result column="login_name" jdbcType="VARCHAR" property="loginName"/>
|
||||
<result column="operation_type" jdbcType="INTEGER" property="operationType"/>
|
||||
<result column="operation_time" jdbcType="TIMESTAMP" property="operationTime"/>
|
||||
</resultMap>
|
||||
|
||||
<delete id="deleteByProcessInstanceId">
|
||||
DELETE FROM zz_flow_message_identity_operation a
|
||||
WHERE EXISTS (SELECT * FROM zz_flow_message b
|
||||
WHERE a.message_id = b.message_id AND b.process_instance_id = #{processInstanceId})
|
||||
</delete>
|
||||
</mapper>
|
||||
@@ -19,6 +19,7 @@
|
||||
<result column="task_assignee" jdbcType="VARCHAR" property="taskAssignee"/>
|
||||
<result column="task_finished" jdbcType="BIT" property="taskFinished"/>
|
||||
<result column="business_data_shot" jdbcType="LONGVARCHAR" property="businessDataShot"/>
|
||||
<result column="online_form_data" jdbcType="BIT" property="onlineFormData"/>
|
||||
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
|
||||
<result column="update_user_id" jdbcType="BIGINT" property="updateUserId"/>
|
||||
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
|
||||
@@ -31,17 +32,60 @@
|
||||
<where>
|
||||
a.task_finished = 0
|
||||
AND a.message_type = 0
|
||||
<if test="groupIdSet == null">
|
||||
AND a.task_assignee = #{loginName}
|
||||
</if>
|
||||
<if test="groupIdSet != null">
|
||||
AND (a.task_assignee = #{loginName} OR EXISTS (SELECT * FROM zz_flow_message_candicate_identity b
|
||||
WHERE a.message_id = b.message_id AND b.candidate_id in
|
||||
<foreach collection="groupIdSet" index="index" item="item" separator="," open="(" close=")">
|
||||
#{item}
|
||||
</foreach>))
|
||||
</where>
|
||||
ORDER BY a.update_time DESC
|
||||
</select>
|
||||
|
||||
<select id="getCopyMessageListByUser" resultMap="BaseResultMap">
|
||||
SELECT a.* FROM zz_flow_message a
|
||||
<where>
|
||||
a.message_type = 1
|
||||
AND EXISTS (SELECT * FROM zz_flow_message_candicate_identity b
|
||||
WHERE a.message_id = b.message_id AND b.candidate_id in
|
||||
<foreach collection="groupIdSet" index="index" item="item" separator="," open="(" close=")">
|
||||
#{item}
|
||||
</foreach>)
|
||||
<if test="!read">
|
||||
AND NOT EXISTS (SELECT * FROM zz_flow_message_identity_operation c
|
||||
WHERE a.message_id = c.message_id AND c.login_name = #{loginName})
|
||||
</if>
|
||||
<if test="read">
|
||||
AND EXISTS (SELECT * FROM zz_flow_message_identity_operation c
|
||||
WHERE a.message_id = c.message_id AND c.login_name = #{loginName})
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY a.update_time DESC
|
||||
</select>
|
||||
|
||||
<select id="countRemindingMessageListByUser" resultType="java.lang.Integer">
|
||||
SELECT COUNT(1) FROM zz_flow_message a
|
||||
<where>
|
||||
a.task_finished = 0
|
||||
AND a.message_type = 0
|
||||
AND (a.task_assignee = #{loginName} OR EXISTS (SELECT * FROM zz_flow_message_candicate_identity b
|
||||
WHERE a.message_id = b.message_id AND b.candidate_id in
|
||||
<foreach collection="groupIdSet" index="index" item="item" separator="," open="(" close=")">
|
||||
#{item}
|
||||
</foreach>))
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="countCopyMessageListByUser" resultType="java.lang.Integer">
|
||||
SELECT COUNT(1) FROM zz_flow_message a
|
||||
<where>
|
||||
a.message_type = 1
|
||||
AND EXISTS (SELECT * FROM zz_flow_message_candicate_identity b
|
||||
WHERE a.message_id = b.message_id AND b.candidate_id in
|
||||
<foreach collection="groupIdSet" index="index" item="item" separator="," open="(" close=")">
|
||||
#{item}
|
||||
</foreach>)
|
||||
AND NOT EXISTS (SELECT * FROM zz_flow_message_identity_operation c
|
||||
WHERE a.message_id = c.message_id AND c.login_name = #{loginName})
|
||||
</where>
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
<result column="task_name" jdbcType="VARCHAR" property="taskName"/>
|
||||
<result column="approval_type" jdbcType="VARCHAR" property="approvalType"/>
|
||||
<result column="delegate_assignee" jdbcType="VARCHAR" property="delegateAssginee"/>
|
||||
<result column="custom_business_data" jdbcType="LONGVARCHAR" property="customBusinessData"/>
|
||||
<result column="comment" jdbcType="VARCHAR" property="comment"/>
|
||||
<result column="create_user_id" jdbcType="BIGINT" property="createUserId"/>
|
||||
<result column="create_login_name" jdbcType="VARCHAR" property="createLoginName"/>
|
||||
<result column="create_username" jdbcType="VARCHAR" property="createUsername"/>
|
||||
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
|
||||
</resultMap>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<result column="role_ids" jdbcType="VARCHAR" property="roleIds"/>
|
||||
<result column="dept_ids" jdbcType="VARCHAR" property="deptIds"/>
|
||||
<result column="candidate_usernames" jdbcType="VARCHAR" property="candidateUsernames"/>
|
||||
<result column="copy_list_json" jdbcType="VARCHAR" property="copyListJson"/>
|
||||
</resultMap>
|
||||
|
||||
<insert id="insertList">
|
||||
@@ -26,7 +27,8 @@
|
||||
#{item.deptPostListJson},
|
||||
#{item.roleIds},
|
||||
#{item.deptIds},
|
||||
#{item.candidateUsernames})
|
||||
#{item.candidateUsernames},
|
||||
#{item.copyListJson})
|
||||
</foreach>
|
||||
</insert>
|
||||
</mapper>
|
||||
|
||||
@@ -35,7 +35,7 @@ public class FlowFinishedListener implements ExecutionListener {
|
||||
flowWorkOrderService.updateFlowStatusByProcessInstanceId(processInstanceId, FlowTaskStatus.FINISHED);
|
||||
String businessKey = execution.getProcessInstanceBusinessKey();
|
||||
FlowWorkOrder workOrder = flowWorkOrderService.getFlowWorkOrderByProcessInstanceId(processInstanceId);
|
||||
flowCustomExtFactory.getDataSyncExtHelper()
|
||||
flowCustomExtFactory.getBusinessDataExtHelper()
|
||||
.triggerSync(workOrder.getProcessDefinitionKey(), processInstanceId, businessKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@ public class FlowEntry {
|
||||
/**
|
||||
* 最新发布时间。
|
||||
*/
|
||||
@TableField(value = "lastest_publish_time")
|
||||
private Date lastestPublishTime;
|
||||
@TableField(value = "latest_publish_time")
|
||||
private Date latestPublishTime;
|
||||
|
||||
/**
|
||||
* 流程状态。
|
||||
|
||||
@@ -123,6 +123,12 @@ public class FlowMessage {
|
||||
@TableField(value = "business_data_shot")
|
||||
private String businessDataShot;
|
||||
|
||||
/**
|
||||
* 是否为在线表单消息数据。
|
||||
*/
|
||||
@TableField(value = "online_form_data")
|
||||
private Boolean onlineFormData;
|
||||
|
||||
/**
|
||||
* 更新时间。
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.orangeforms.common.flow.model;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 流程任务消息所属用户的操作表。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2021-06-06
|
||||
*/
|
||||
@Data
|
||||
@TableName(value = "zz_flow_message_identity_operation")
|
||||
public class FlowMessageIdentityOperation {
|
||||
|
||||
/**
|
||||
* 主键Id。
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 任务消息Id。
|
||||
*/
|
||||
@TableField(value = "message_id")
|
||||
private Long messageId;
|
||||
|
||||
/**
|
||||
* 用户登录名。
|
||||
*/
|
||||
@TableField(value = "login_name")
|
||||
private String loginName;
|
||||
|
||||
/**
|
||||
* 操作类型。
|
||||
* 常量值参考FlowMessageOperationType对象。
|
||||
*/
|
||||
@TableField(value = "operation_type")
|
||||
private Integer operationType;
|
||||
|
||||
/**
|
||||
* 操作时间。
|
||||
*/
|
||||
@TableField(value = "operation_time")
|
||||
private Date operationTime;
|
||||
}
|
||||
@@ -70,12 +70,24 @@ public class FlowTaskComment {
|
||||
@TableField(value = "delegate_assignee")
|
||||
private String delegateAssginee;
|
||||
|
||||
/**
|
||||
* 自定义数据。开发者可自行扩展,推荐使用JSON格式数据。
|
||||
*/
|
||||
@TableField(value = "custom_business_data")
|
||||
private String customBusinessData;
|
||||
|
||||
/**
|
||||
* 创建者Id。
|
||||
*/
|
||||
@TableField(value = "create_user_id")
|
||||
private Long createUserId;
|
||||
|
||||
/**
|
||||
* 创建者登录名。
|
||||
*/
|
||||
@TableField(value = "create_login_name")
|
||||
private String createLoginName;
|
||||
|
||||
/**
|
||||
* 创建者显示名。
|
||||
*/
|
||||
|
||||
@@ -72,4 +72,10 @@ public class FlowTaskExt {
|
||||
*/
|
||||
@TableField(value = "candidate_usernames")
|
||||
private String candidateUsernames;
|
||||
|
||||
/**
|
||||
* 抄送相关的数据。
|
||||
*/
|
||||
@TableField(value = "copy_list_json")
|
||||
private String copyListJson;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.orangeforms.common.flow.model.constant;
|
||||
|
||||
/**
|
||||
* 工作流消息操作类型。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2021-06-06
|
||||
*/
|
||||
public final class FlowMessageOperationType {
|
||||
|
||||
/**
|
||||
* 已读操作。
|
||||
*/
|
||||
public static final int READ_FINISHED = 0;
|
||||
|
||||
/**
|
||||
* 私有构造函数,明确标识该常量类的作用。
|
||||
*/
|
||||
private FlowMessageOperationType() {
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,15 @@ public final class FlowMessageType {
|
||||
*/
|
||||
public static final int REMIND_TYPE = 0;
|
||||
|
||||
/**
|
||||
* 抄送消息。
|
||||
*/
|
||||
public static final int COPY_TYPE = 1;
|
||||
|
||||
private static final Map<Object, String> DICT_MAP = new HashMap<>(2);
|
||||
static {
|
||||
DICT_MAP.put(REMIND_TYPE, "催办消息");
|
||||
DICT_MAP.put(COPY_TYPE, "抄送消息");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -402,12 +402,13 @@ public interface FlowApiService {
|
||||
BpmnModel convertToBpmnModel(String bpmnXml) throws XMLStreamException;
|
||||
|
||||
/**
|
||||
* 回退到上一个用户任务节点。
|
||||
* 回退到上一个用户任务节点。如果没有指定,则回退到上一个任务。
|
||||
*
|
||||
* @param task 当前活动任务。
|
||||
* @param targetKey 指定回退到的任务标识。如果为null,则回退到上一个任务。
|
||||
* @param forReject true表示驳回,false为撤回。
|
||||
* @param reason 驳回或者撤销的原因。
|
||||
* @return 回退结果。
|
||||
*/
|
||||
CallResult backToLastRuntimeTask(Task task, boolean forReject, String reason);
|
||||
CallResult backToRuntimeTask(Task task, String targetKey, boolean forReject, String reason);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.orangeforms.common.flow.service;
|
||||
|
||||
import com.orangeforms.common.core.base.service.IBaseService;
|
||||
import com.orangeforms.common.core.object.CallResult;
|
||||
import com.orangeforms.common.flow.model.*;
|
||||
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.orangeforms.common.flow.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.orangeforms.common.core.base.service.IBaseService;
|
||||
import com.orangeforms.common.flow.model.FlowMessage;
|
||||
import com.orangeforms.common.flow.model.FlowWorkOrder;
|
||||
import org.flowable.task.api.Task;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -29,6 +31,14 @@ public interface FlowMessageService extends IBaseService<FlowMessage, Long> {
|
||||
*/
|
||||
void saveNewRemindMessage(FlowWorkOrder flowWorkOrder);
|
||||
|
||||
/**
|
||||
* 保存抄送消息对象。
|
||||
*
|
||||
* @param task 待抄送的任务。
|
||||
* @param copyDataJson 抄送人员或者组的Id数据。
|
||||
*/
|
||||
void saveNewCopyMessage(Task task, JSONObject copyDataJson);
|
||||
|
||||
/**
|
||||
* 更新指定运行时任务Id的消费为已完成状态。
|
||||
*
|
||||
@@ -49,4 +59,48 @@ public interface FlowMessageService extends IBaseService<FlowMessage, Long> {
|
||||
* @return 查询后的催办消息列表。
|
||||
*/
|
||||
List<FlowMessage> getRemindingMessageListByUser();
|
||||
|
||||
/**
|
||||
* 获取当前用户的抄送消息列表。
|
||||
*
|
||||
* @param read true表示已读,false表示未读。
|
||||
* @return 查询后的抄送消息列表。
|
||||
*/
|
||||
List<FlowMessage> getCopyMessageListByUser(Boolean read);
|
||||
|
||||
/**
|
||||
* 判断当前用户是否有权限访问指定消息Id。
|
||||
*
|
||||
* @param messageId 消息Id。
|
||||
* @return true为合法访问者,否则false。
|
||||
*/
|
||||
boolean isCandidateIdentityOnMessage(Long messageId);
|
||||
|
||||
/**
|
||||
* 读取抄送消息,同时更新当前用户对指定抄送消息的读取状态。
|
||||
*
|
||||
* @param messageId 消息Id。
|
||||
*/
|
||||
void readCopyTask(Long messageId);
|
||||
|
||||
/**
|
||||
* 计算当前用户催办消息的数量。
|
||||
*
|
||||
* @return 当前用户催办消息数量。
|
||||
*/
|
||||
int countRemindingMessageListByUser();
|
||||
|
||||
/**
|
||||
* 计算当前用户未读抄送消息的数量。
|
||||
*
|
||||
* @return 当前用户未读抄送消息数量。
|
||||
*/
|
||||
int countCopyMessageByUser();
|
||||
|
||||
/**
|
||||
* 删除指定流程实例的消息。
|
||||
*
|
||||
* @param processInstanceId 流程实例Id。
|
||||
*/
|
||||
void removeByProcessInstanceId(String processInstanceId);
|
||||
}
|
||||
|
||||
@@ -44,4 +44,21 @@ public interface FlowTaskCommentService extends IBaseService<FlowTaskComment, Lo
|
||||
* @return 查询结果。
|
||||
*/
|
||||
FlowTaskComment getLatestFlowTaskComment(String processInstanceId);
|
||||
|
||||
/**
|
||||
* 获取指定流程实例和任务定义标识的最后一条审批任务。
|
||||
*
|
||||
* @param processInstanceId 流程实例Id。
|
||||
* @param taskDefinitionKey 任务定义标识。
|
||||
* @return 查询结果。
|
||||
*/
|
||||
FlowTaskComment getLatestFlowTaskComment(String processInstanceId, String taskDefinitionKey);
|
||||
|
||||
/**
|
||||
* 获取指定流程实例的第一条审批任务。
|
||||
*
|
||||
* @param processInstanceId 流程实例Id。
|
||||
* @return 查询结果。
|
||||
*/
|
||||
FlowTaskComment getFirstFlowTaskComment(String processInstanceId);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.orangeforms.common.flow.service;
|
||||
|
||||
import com.orangeforms.common.core.base.service.IBaseService;
|
||||
import com.orangeforms.common.flow.model.FlowWorkOrder;
|
||||
import com.orangeforms.common.flow.vo.FlowWorkOrderVo;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
|
||||
import java.util.*;
|
||||
@@ -83,4 +84,19 @@ public interface FlowWorkOrderService extends IBaseService<FlowWorkOrder, Long>
|
||||
* @param flowStatus 新的流程状态值。
|
||||
*/
|
||||
void updateFlowStatusByProcessInstanceId(String processInstanceId, int flowStatus);
|
||||
|
||||
/**
|
||||
* 是否有查看该工单的数据权限。
|
||||
* @param processInstanceId 流程实例Id。
|
||||
* @return 存在返回true,否则false。
|
||||
*/
|
||||
boolean hasDataPermOnFlowWorkOrder(String processInstanceId);
|
||||
|
||||
/**
|
||||
* 根据工单列表中的submitUserName,找到映射的userShowName,并会写到Vo中指定字段。
|
||||
* 同时这也是一个如何通过插件方法,将loginName映射到showName的示例,
|
||||
*
|
||||
* @param dataList 工单Vo对象列表。
|
||||
*/
|
||||
void fillUserShowNameByLoginName(List<FlowWorkOrderVo> dataList);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.orangeforms.common.core.object.CallResult;
|
||||
import com.orangeforms.common.core.object.MyPageData;
|
||||
import com.orangeforms.common.core.object.MyPageParam;
|
||||
import com.orangeforms.common.core.object.TokenData;
|
||||
import com.orangeforms.common.core.util.MyDateUtil;
|
||||
import com.orangeforms.common.flow.constant.FlowConstant;
|
||||
import com.orangeforms.common.flow.constant.FlowApprovalType;
|
||||
import com.orangeforms.common.flow.constant.FlowTaskStatus;
|
||||
@@ -38,6 +39,7 @@ import org.flowable.engine.history.*;
|
||||
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
|
||||
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.flowable.engine.runtime.ChangeActivityStateBuilder;
|
||||
import org.flowable.engine.runtime.Execution;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.flowable.task.api.Task;
|
||||
@@ -90,9 +92,14 @@ public class FlowApiServiceImpl implements FlowApiService {
|
||||
@Override
|
||||
public ProcessInstance start(String processDefinitionId, Object dataId) {
|
||||
String loginName = TokenData.takeFromRequest().getLoginName();
|
||||
Map<String, Object> variableMap = new HashMap<>(4);
|
||||
variableMap.put(FlowConstant.PROC_INSTANCE_INITIATOR_VAR, loginName);
|
||||
variableMap.put(FlowConstant.PROC_INSTANCE_START_USER_NAME_VAR, loginName);
|
||||
Map<String, Object> variableMap;
|
||||
if (dataId == null) {
|
||||
variableMap = new HashMap<>(2);
|
||||
variableMap.put(FlowConstant.PROC_INSTANCE_INITIATOR_VAR, loginName);
|
||||
variableMap.put(FlowConstant.PROC_INSTANCE_START_USER_NAME_VAR, loginName);
|
||||
} else {
|
||||
variableMap = this.initAndGetProcessInstanceVariables(processDefinitionId);
|
||||
}
|
||||
Authentication.setAuthenticatedUserId(loginName);
|
||||
String businessKey = dataId == null ? null : dataId.toString();
|
||||
return runtimeService.startProcessInstanceById(processDefinitionId, businessKey, variableMap);
|
||||
@@ -133,8 +140,8 @@ public class FlowApiServiceImpl implements FlowApiService {
|
||||
FlowTaskComment flowTaskComment = new FlowTaskComment();
|
||||
flowTaskComment.fillWith(startTaskInstance);
|
||||
flowTaskComment.setApprovalType(FlowApprovalType.MULTI_CONSIGN);
|
||||
String loginName = TokenData.takeFromRequest().getLoginName();
|
||||
String comment = String.format("用户 [%s] 加签 [%s]。", loginName, newAssignees);
|
||||
String showName = TokenData.takeFromRequest().getLoginName();
|
||||
String comment = String.format("用户 [%s] 加签 [%s]。", showName, newAssignees);
|
||||
flowTaskComment.setComment(comment);
|
||||
flowTaskCommentService.saveNew(flowTaskComment);
|
||||
return;
|
||||
@@ -143,6 +150,10 @@ public class FlowApiServiceImpl implements FlowApiService {
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void completeTask(Task task, FlowTaskComment flowTaskComment, JSONObject taskVariableData) {
|
||||
JSONObject passCopyData = null;
|
||||
if (taskVariableData != null) {
|
||||
passCopyData = (JSONObject) taskVariableData.remove(FlowConstant.COPY_DATA_KEY);
|
||||
}
|
||||
if (flowTaskComment != null) {
|
||||
// 这里处理多实例会签逻辑。
|
||||
if (flowTaskComment.getApprovalType().equals(FlowApprovalType.MULTI_SIGN)) {
|
||||
@@ -183,13 +194,80 @@ public class FlowApiServiceImpl implements FlowApiService {
|
||||
taskVariableData.put(FlowConstant.OPERATION_TYPE_VAR, flowTaskComment.getApprovalType());
|
||||
flowTaskComment.fillWith(task);
|
||||
flowTaskCommentService.saveNew(flowTaskComment);
|
||||
taskService.complete(task.getId(), taskVariableData);
|
||||
} else {
|
||||
taskService.complete(task.getId(), taskVariableData);
|
||||
}
|
||||
// 判断当前完成执行的任务,是否存在抄送设置。
|
||||
Object copyData = runtimeService.getVariable(
|
||||
task.getProcessInstanceId(), FlowConstant.COPY_DATA_MAP_PREFIX + task.getTaskDefinitionKey());
|
||||
if (copyData != null || passCopyData != null) {
|
||||
JSONObject copyDataJson = this.mergeCopyData(copyData, passCopyData);
|
||||
flowMessageService.saveNewCopyMessage(task, copyDataJson);
|
||||
}
|
||||
taskService.complete(task.getId(), taskVariableData);
|
||||
flowMessageService.updateFinishedStatusByTaskId(task.getId());
|
||||
}
|
||||
|
||||
private JSONObject mergeCopyData(Object copyData, JSONObject passCopyData) {
|
||||
TokenData tokenData = TokenData.takeFromRequest();
|
||||
// passCopyData是传阅数据,copyData是抄送数据。
|
||||
JSONObject resultCopyDataJson = passCopyData;
|
||||
if (resultCopyDataJson == null) {
|
||||
resultCopyDataJson = JSON.parseObject(copyData.toString());
|
||||
} else if (copyData != null) {
|
||||
JSONObject copyDataJson = JSON.parseObject(copyData.toString());
|
||||
for (Map.Entry<String, Object> entry : copyDataJson.entrySet()) {
|
||||
String value = resultCopyDataJson.getString(entry.getKey());
|
||||
if (value == null) {
|
||||
resultCopyDataJson.put(entry.getKey(), entry.getValue());
|
||||
} else {
|
||||
List<String> list1 = StrUtil.split(value, ",");
|
||||
List<String> list2 = StrUtil.split(entry.getValue().toString(), ",");
|
||||
Set<String> valueSet = new HashSet<>(list1);
|
||||
valueSet.addAll(list2);
|
||||
resultCopyDataJson.put(entry.getKey(), StrUtil.join(",", valueSet));
|
||||
}
|
||||
}
|
||||
}
|
||||
BaseFlowIdentityExtHelper flowIdentityExtHelper = flowCustomExtFactory.getFlowIdentityExtHelper();
|
||||
for (Map.Entry<String, Object> entry : resultCopyDataJson.entrySet()) {
|
||||
String type = entry.getKey();
|
||||
switch (type) {
|
||||
case FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER_VAR:
|
||||
Object upLeaderDeptPostId =
|
||||
flowIdentityExtHelper.getUpLeaderDeptPostId(tokenData.getDeptId());
|
||||
entry.setValue(upLeaderDeptPostId);
|
||||
break;
|
||||
case FlowConstant.GROUP_TYPE_DEPT_POST_LEADER_VAR:
|
||||
Object leaderDeptPostId =
|
||||
flowIdentityExtHelper.getLeaderDeptPostId(tokenData.getDeptId());
|
||||
entry.setValue(leaderDeptPostId);
|
||||
break;
|
||||
case FlowConstant.GROUP_TYPE_SELF_DEPT_POST_VAR:
|
||||
Set<String> selfPostIdSet = new HashSet<>(StrUtil.split(entry.getValue().toString(), ","));
|
||||
Map<String, String> deptPostIdMap =
|
||||
flowIdentityExtHelper.getDeptPostIdMap(tokenData.getDeptId(), selfPostIdSet);
|
||||
String deptPostIdValues = "";
|
||||
if (deptPostIdMap != null) {
|
||||
deptPostIdValues = StrUtil.join(",", deptPostIdMap.values());
|
||||
}
|
||||
entry.setValue(deptPostIdValues);
|
||||
break;
|
||||
case FlowConstant.GROUP_TYPE_UP_DEPT_POST_VAR:
|
||||
Set<String> upPostIdSet = new HashSet<>(StrUtil.split(entry.getValue().toString(), ","));
|
||||
Map<String, String> upDeptPostIdMap =
|
||||
flowIdentityExtHelper.getUpDeptPostIdMap(tokenData.getDeptId(), upPostIdSet);
|
||||
String upDeptPostIdValues = "";
|
||||
if (upDeptPostIdMap != null) {
|
||||
upDeptPostIdValues = StrUtil.join(",", upDeptPostIdMap.values());
|
||||
}
|
||||
entry.setValue(upDeptPostIdValues);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return resultCopyDataJson;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public CallResult verifyAssigneeOrCandidateAndClaim(Task task) {
|
||||
@@ -257,9 +335,27 @@ public class FlowApiServiceImpl implements FlowApiService {
|
||||
this.buildPostCandidateGroupData(flowIdentityExtHelper, flowTaskExtList);
|
||||
variableMap.putAll(postGroupDataMap);
|
||||
}
|
||||
this.buildCopyData(flowTaskExtList, variableMap);
|
||||
return variableMap;
|
||||
}
|
||||
|
||||
private void buildCopyData(List<FlowTaskExt> flowTaskExtList, Map<String, Object> variableMap) {
|
||||
TokenData tokenData = TokenData.takeFromRequest();
|
||||
for (FlowTaskExt flowTaskExt : flowTaskExtList) {
|
||||
if (StrUtil.isBlank(flowTaskExt.getCopyListJson())) {
|
||||
continue;
|
||||
}
|
||||
List<JSONObject> copyDataList = JSON.parseArray(flowTaskExt.getCopyListJson(), JSONObject.class);
|
||||
Map<String, Object> copyDataMap = new HashMap<>(copyDataList.size());
|
||||
for (JSONObject copyData : copyDataList) {
|
||||
String type = copyData.getString("type");
|
||||
String id = copyData.getString("id");
|
||||
copyDataMap.put(type, id == null ? "" : id);
|
||||
}
|
||||
variableMap.put(FlowConstant.COPY_DATA_MAP_PREFIX + flowTaskExt.getTaskId(), JSON.toJSONString(copyDataMap));
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> buildPostCandidateGroupData(
|
||||
BaseFlowIdentityExtHelper flowIdentityExtHelper, List<FlowTaskExt> flowTaskExtList) {
|
||||
Map<String, Object> postVariableMap = new HashMap<>();
|
||||
@@ -534,11 +630,11 @@ public class FlowApiServiceImpl implements FlowApiService {
|
||||
query.startedBy(startUser);
|
||||
}
|
||||
if (StrUtil.isNotBlank(beginDate)) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(MyDateUtil.COMMON_SHORT_DATETIME_FORMAT);
|
||||
query.startedAfter(sdf.parse(beginDate));
|
||||
}
|
||||
if (StrUtil.isNotBlank(endDate)) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(MyDateUtil.COMMON_SHORT_DATETIME_FORMAT);
|
||||
query.startedBefore(sdf.parse(endDate));
|
||||
}
|
||||
if (finishedOnly) {
|
||||
@@ -565,11 +661,11 @@ public class FlowApiServiceImpl implements FlowApiService {
|
||||
query.processDefinitionName(processDefinitionName);
|
||||
}
|
||||
if (StrUtil.isNotBlank(beginDate)) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(MyDateUtil.COMMON_SHORT_DATETIME_FORMAT);
|
||||
query.taskCompletedAfter(sdf.parse(beginDate));
|
||||
}
|
||||
if (StrUtil.isNotBlank(endDate)) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(MyDateUtil.COMMON_SHORT_DATETIME_FORMAT);
|
||||
query.taskCompletedBefore(sdf.parse(endDate));
|
||||
}
|
||||
query.orderByHistoricTaskInstanceEndTime().desc();
|
||||
@@ -664,6 +760,7 @@ public class FlowApiServiceImpl implements FlowApiService {
|
||||
public void deleteProcessInstance(String processInstanceId) {
|
||||
historyService.deleteHistoricProcessInstance(processInstanceId);
|
||||
flowWorkOrderService.removeByProcessInstanceId(processInstanceId);
|
||||
flowMessageService.removeByProcessInstanceId(processInstanceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -681,80 +778,95 @@ public class FlowApiServiceImpl implements FlowApiService {
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CallResult backToLastRuntimeTask(Task task, boolean forReject, String reason) {
|
||||
public CallResult backToRuntimeTask(Task task, String targetKey, boolean forReject, String reason) {
|
||||
String errorMessage;
|
||||
ProcessDefinition processDefinition = this.getProcessDefinitionById(task.getProcessDefinitionId());
|
||||
Collection<FlowElement> allElements = this.getProcessAllElements(processDefinition.getId());
|
||||
FlowElement source = null;
|
||||
// 获取跳转的节点元素
|
||||
FlowElement target = null;
|
||||
for (FlowElement flowElement : allElements) {
|
||||
if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
|
||||
source = flowElement;
|
||||
break;
|
||||
if (StrUtil.isBlank(targetKey)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (StrUtil.isNotBlank(targetKey)) {
|
||||
if (flowElement.getId().equals(targetKey)) {
|
||||
target = flowElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
List<UserTask> parentUserTaskList =
|
||||
this.getParentUserTaskList(source, null, null);
|
||||
if (CollUtil.isEmpty(parentUserTaskList)) {
|
||||
errorMessage = "数据验证失败,当前节点为初始任务节点,不能驳回!";
|
||||
if (targetKey != null && target == null) {
|
||||
errorMessage = "数据验证失败,被驳回的指定目标节点不存在!";
|
||||
return CallResult.error(errorMessage);
|
||||
}
|
||||
// 获取活动ID, 即节点Key
|
||||
Set<String> parentUserTaskKeySet = new HashSet<>();
|
||||
parentUserTaskList.forEach(item -> parentUserTaskKeySet.add(item.getId()));
|
||||
List<HistoricActivityInstance> historicActivityIdList =
|
||||
this.getHistoricActivityInstanceListOrderByStartTime(task.getProcessInstanceId());
|
||||
// 数据清洗,将回滚导致的脏数据清洗掉
|
||||
List<String> lastHistoricTaskInstanceList =
|
||||
this.cleanHistoricTaskInstance(allElements, historicActivityIdList);
|
||||
// 此时历史任务实例为倒序,获取最后走的节点
|
||||
List<String> targetIds = new ArrayList<>();
|
||||
// 循环结束标识,遇到当前目标节点的次数
|
||||
int number = 0;
|
||||
StringBuilder parentHistoricTaskKey = new StringBuilder();
|
||||
for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) {
|
||||
// 当会签时候会出现特殊的,连续都是同一个节点历史数据的情况,这种时候跳过
|
||||
if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) {
|
||||
continue;
|
||||
UserTask oneUserTask = null;
|
||||
List<String> targetIds = null;
|
||||
if (target == null) {
|
||||
List<UserTask> parentUserTaskList =
|
||||
this.getParentUserTaskList(source, null, null);
|
||||
if (CollUtil.isEmpty(parentUserTaskList)) {
|
||||
errorMessage = "数据验证失败,当前节点为初始任务节点,不能驳回!";
|
||||
return CallResult.error(errorMessage);
|
||||
}
|
||||
parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey);
|
||||
if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) {
|
||||
number++;
|
||||
}
|
||||
if (number == 2) {
|
||||
break;
|
||||
}
|
||||
// 如果当前历史节点,属于父级的节点,说明最后一次经过了这个点,需要退回这个点
|
||||
if (parentUserTaskKeySet.contains(historicTaskInstanceKey)) {
|
||||
targetIds.add(historicTaskInstanceKey);
|
||||
// 获取活动ID, 即节点Key
|
||||
Set<String> parentUserTaskKeySet = new HashSet<>();
|
||||
parentUserTaskList.forEach(item -> parentUserTaskKeySet.add(item.getId()));
|
||||
List<HistoricActivityInstance> historicActivityIdList =
|
||||
this.getHistoricActivityInstanceListOrderByStartTime(task.getProcessInstanceId());
|
||||
// 数据清洗,将回滚导致的脏数据清洗掉
|
||||
List<String> lastHistoricTaskInstanceList =
|
||||
this.cleanHistoricTaskInstance(allElements, historicActivityIdList);
|
||||
// 此时历史任务实例为倒序,获取最后走的节点
|
||||
targetIds = new ArrayList<>();
|
||||
// 循环结束标识,遇到当前目标节点的次数
|
||||
int number = 0;
|
||||
StringBuilder parentHistoricTaskKey = new StringBuilder();
|
||||
for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) {
|
||||
// 当会签时候会出现特殊的,连续都是同一个节点历史数据的情况,这种时候跳过
|
||||
if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) {
|
||||
continue;
|
||||
}
|
||||
parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey);
|
||||
if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) {
|
||||
number++;
|
||||
}
|
||||
if (number == 2) {
|
||||
break;
|
||||
}
|
||||
// 如果当前历史节点,属于父级的节点,说明最后一次经过了这个点,需要退回这个点
|
||||
if (parentUserTaskKeySet.contains(historicTaskInstanceKey)) {
|
||||
targetIds.add(historicTaskInstanceKey);
|
||||
}
|
||||
}
|
||||
// 目的获取所有需要被跳转的节点 currentIds
|
||||
// 取其中一个父级任务,因为后续要么存在公共网关,要么就是串行公共线路
|
||||
oneUserTask = parentUserTaskList.get(0);
|
||||
}
|
||||
// 目的获取所有需要被跳转的节点 currentIds
|
||||
// 取其中一个父级任务,因为后续要么存在公共网关,要么就是串行公共线路
|
||||
UserTask oneUserTask = parentUserTaskList.get(0);
|
||||
// 获取所有正常进行的执行任务的活动节点ID,这些任务不能直接使用,需要找出其中需要撤回的任务
|
||||
List<Execution> runExecutionList =
|
||||
runtimeService.createExecutionQuery().processInstanceId(task.getProcessInstanceId()).list();
|
||||
|
||||
List<String> runActivityIdList = new ArrayList<>();
|
||||
runExecutionList.forEach(item -> {
|
||||
if (StrUtil.isNotBlank(item.getActivityId())) {
|
||||
runActivityIdList.add(item.getActivityId());
|
||||
}
|
||||
});
|
||||
List<String> runActivityIdList = runExecutionList.stream()
|
||||
.filter(c -> StrUtil.isNotBlank(c.getActivityId()))
|
||||
.map(Execution::getActivityId).collect(Collectors.toList());
|
||||
// 需驳回任务列表
|
||||
List<String> currentIds = new ArrayList<>();
|
||||
// 通过父级网关的出口连线,结合 runExecutionList 比对,获取需要撤回的任务
|
||||
List<FlowElement> currentFlowElementList =
|
||||
this.getChildUserTaskList(oneUserTask, runActivityIdList, null, null);
|
||||
List<FlowElement> currentFlowElementList = this.getChildUserTaskList(
|
||||
target != null ? target : oneUserTask, runActivityIdList, null, null);
|
||||
currentFlowElementList.forEach(item -> currentIds.add(item.getId()));
|
||||
// 规定:并行网关之前节点必须需存在唯一用户任务节点,如果出现多个任务节点,则并行网关节点默认为结束节点,原因为不考虑多对多情况
|
||||
if (targetIds.size() > 1 && currentIds.size() > 1) {
|
||||
errorMessage = "数据验证失败,任务出现多对多情况,无法撤回!";
|
||||
return CallResult.error(errorMessage);
|
||||
if (target == null) {
|
||||
// 规定:并行网关之前节点必须需存在唯一用户任务节点,如果出现多个任务节点,则并行网关节点默认为结束节点,原因为不考虑多对多情况
|
||||
if (targetIds.size() > 1 && currentIds.size() > 1) {
|
||||
errorMessage = "数据验证失败,任务出现多对多情况,无法撤回!";
|
||||
return CallResult.error(errorMessage);
|
||||
}
|
||||
}
|
||||
AtomicReference<List<HistoricActivityInstance>> tmp = new AtomicReference<>();
|
||||
// 用于下面新增网关删除信息时使用
|
||||
String targetTmp = String.join(",", targetIds);
|
||||
String targetTmp = targetKey != null ? targetKey : String.join(",", targetIds);
|
||||
// currentIds 为活动ID列表
|
||||
// currentExecutionIds 为执行任务ID列表
|
||||
// 需要通过执行任务ID来设置驳回信息,活动ID不行
|
||||
@@ -778,19 +890,43 @@ public class FlowApiServiceImpl implements FlowApiService {
|
||||
}
|
||||
}));
|
||||
try {
|
||||
// 如果父级任务多于 1 个,说明当前节点不是并行节点,原因为不考虑多对多情况
|
||||
if (targetIds.size() > 1) {
|
||||
// 1 对 多任务跳转,currentIds 当前节点(1),targetIds 跳转到的节点(多)
|
||||
if (StrUtil.isNotBlank(targetKey)) {
|
||||
runtimeService.createChangeActivityStateBuilder()
|
||||
.processInstanceId(task.getProcessInstanceId())
|
||||
.moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState();
|
||||
}
|
||||
// 如果父级任务只有一个,因此当前任务可能为网关中的任务
|
||||
if (targetIds.size() == 1) {
|
||||
// 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetIds.get(0) 跳转到的节点(1)
|
||||
runtimeService.createChangeActivityStateBuilder()
|
||||
.processInstanceId(task.getProcessInstanceId())
|
||||
.moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState();
|
||||
.moveActivityIdsToSingleActivityId(currentIds, targetKey).changeState();
|
||||
} else {
|
||||
// 如果父级任务多于 1 个,说明当前节点不是并行节点,原因为不考虑多对多情况
|
||||
if (targetIds.size() > 1) {
|
||||
// 1 对 多任务跳转,currentIds 当前节点(1),targetIds 跳转到的节点(多)
|
||||
ChangeActivityStateBuilder builder = runtimeService.createChangeActivityStateBuilder()
|
||||
.processInstanceId(task.getProcessInstanceId())
|
||||
.moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds);
|
||||
for (String targetId : targetIds) {
|
||||
FlowTaskComment taskComment =
|
||||
flowTaskCommentService.getLatestFlowTaskComment(task.getProcessInstanceId(), targetId);
|
||||
// 如果驳回后的目标任务包含指定人,则直接通过变量回抄,如果没有则自动忽略该变量,不会给流程带来任何影响。
|
||||
String submitLoginName = taskComment.getCreateLoginName();
|
||||
if (StrUtil.isNotBlank(submitLoginName)) {
|
||||
builder.localVariable(targetId, FlowConstant.TASK_APPOINTED_ASSIGNEE_VAR, submitLoginName);
|
||||
}
|
||||
}
|
||||
builder.changeState();
|
||||
}
|
||||
// 如果父级任务只有一个,因此当前任务可能为网关中的任务
|
||||
if (targetIds.size() == 1) {
|
||||
// 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetIds.get(0) 跳转到的节点(1)
|
||||
// 如果驳回后的目标任务包含指定人,则直接通过变量回抄,如果没有则自动忽略该变量,不会给流程带来任何影响。
|
||||
ChangeActivityStateBuilder builder = runtimeService.createChangeActivityStateBuilder()
|
||||
.processInstanceId(task.getProcessInstanceId())
|
||||
.moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0));
|
||||
FlowTaskComment taskComment =
|
||||
flowTaskCommentService.getLatestFlowTaskComment(task.getProcessInstanceId(), targetIds.get(0));
|
||||
String submitLoginName = taskComment.getCreateLoginName();
|
||||
if (StrUtil.isNotBlank(submitLoginName)) {
|
||||
builder.localVariable(targetIds.get(0), FlowConstant.TASK_APPOINTED_ASSIGNEE_VAR, submitLoginName);
|
||||
}
|
||||
builder.changeState();
|
||||
}
|
||||
}
|
||||
FlowTaskComment comment = new FlowTaskComment();
|
||||
comment.setTaskId(task.getId());
|
||||
@@ -1091,15 +1227,15 @@ public class FlowApiServiceImpl implements FlowApiService {
|
||||
}
|
||||
String roleIds = tokenData.getRoleIds();
|
||||
if (StrUtil.isNotBlank(tokenData.getRoleIds())) {
|
||||
groupIdSet.addAll(Arrays.asList(StrUtil.split(roleIds, ",")));
|
||||
groupIdSet.addAll(StrUtil.split(roleIds, ","));
|
||||
}
|
||||
String postIds = tokenData.getPostIds();
|
||||
if (StrUtil.isNotBlank(tokenData.getPostIds())) {
|
||||
groupIdSet.addAll(Arrays.asList(StrUtil.split(postIds, ",")));
|
||||
groupIdSet.addAll(StrUtil.split(postIds, ","));
|
||||
}
|
||||
String deptPostIds = tokenData.getDeptPostIds();
|
||||
if (StrUtil.isNotBlank(deptPostIds)) {
|
||||
groupIdSet.addAll(Arrays.asList(StrUtil.split(deptPostIds, ",")));
|
||||
groupIdSet.addAll(StrUtil.split(deptPostIds, ","));
|
||||
}
|
||||
if (CollUtil.isNotEmpty(groupIdSet)) {
|
||||
query.or().taskCandidateGroupIn(groupIdSet).taskCandidateOrAssigned(loginName).endOr();
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.orangeforms.common.flow.service.impl;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.github.pagehelper.Page;
|
||||
import com.orangeforms.common.flow.object.FlowTaskPostCandidateGroup;
|
||||
@@ -147,7 +147,7 @@ public class FlowEntryServiceImpl extends BaseService<FlowEntry, Long> implement
|
||||
} else if (StrUtil.equals(t.getGroupType(), FlowConstant.GROUP_TYPE_POST)) {
|
||||
Assert.notNull(t.getDeptPostListJson());
|
||||
List<FlowTaskPostCandidateGroup> groupDataList =
|
||||
JSONArray.parseArray(t.getDeptPostListJson(), FlowTaskPostCandidateGroup.class);
|
||||
JSON.parseArray(t.getDeptPostListJson(), FlowTaskPostCandidateGroup.class);
|
||||
List<String> candidateGroupList =
|
||||
FlowTaskPostCandidateGroup.buildCandidateGroupList(groupDataList);
|
||||
userTask.setCandidateGroups(candidateGroupList);
|
||||
@@ -176,7 +176,7 @@ public class FlowEntryServiceImpl extends BaseService<FlowEntry, Long> implement
|
||||
FlowEntry updatedFlowEntry = new FlowEntry();
|
||||
updatedFlowEntry.setEntryId(flowEntry.getEntryId());
|
||||
updatedFlowEntry.setStatus(FlowEntryStatus.PUBLISHED);
|
||||
updatedFlowEntry.setLastestPublishTime(new Date());
|
||||
updatedFlowEntry.setLatestPublishTime(new Date());
|
||||
// 对于从未发布过的工作,第一次发布的时候会将本地发布置位主版本。
|
||||
if (flowEntry.getStatus().equals(FlowEntryStatus.UNPUBLISHED)) {
|
||||
updatedFlowEntry.setMainEntryPublishId(flowEntryPublish.getEntryPublishId());
|
||||
|
||||
@@ -3,26 +3,31 @@ package com.orangeforms.common.flow.service.impl;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
|
||||
import com.orangeforms.common.core.base.service.BaseService;
|
||||
import com.orangeforms.common.core.object.TokenData;
|
||||
import com.orangeforms.common.flow.constant.FlowConstant;
|
||||
import com.orangeforms.common.flow.model.*;
|
||||
import com.orangeforms.common.flow.model.constant.FlowMessageOperationType;
|
||||
import com.orangeforms.common.flow.model.constant.FlowMessageType;
|
||||
import com.orangeforms.common.flow.dao.FlowMessageIdentityOperationMapper;
|
||||
import com.orangeforms.common.flow.dao.FlowMessageCandidateIdentityMapper;
|
||||
import com.orangeforms.common.flow.dao.FlowMessageMapper;
|
||||
import com.orangeforms.common.flow.model.FlowMessage;
|
||||
import com.orangeforms.common.flow.model.FlowMessageCandidateIdentity;
|
||||
import com.orangeforms.common.flow.model.FlowTaskExt;
|
||||
import com.orangeforms.common.flow.model.FlowWorkOrder;
|
||||
import com.orangeforms.common.flow.object.FlowTaskPostCandidateGroup;
|
||||
import com.orangeforms.common.flow.service.FlowApiService;
|
||||
import com.orangeforms.common.flow.service.FlowMessageService;
|
||||
import com.orangeforms.common.flow.service.FlowTaskExtService;
|
||||
import com.orangeforms.common.flow.util.FlowCustomExtFactory;
|
||||
import com.orangeforms.common.flow.util.BaseBusinessDataExtHelper;
|
||||
import com.orangeforms.common.flow.vo.TaskInfoVo;
|
||||
import com.orangeforms.common.sequence.wrapper.IdGeneratorWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -45,10 +50,14 @@ public class FlowMessageServiceImpl extends BaseService<FlowMessage, Long> imple
|
||||
@Autowired
|
||||
private FlowMessageCandidateIdentityMapper flowMessageCandidateIdentityMapper;
|
||||
@Autowired
|
||||
private FlowMessageIdentityOperationMapper flowMessageIdentityOperationMapper;
|
||||
@Autowired
|
||||
private FlowTaskExtService flowTaskExtService;
|
||||
@Autowired
|
||||
private FlowApiService flowApiService;
|
||||
@Autowired
|
||||
private FlowCustomExtFactory flowCustomExtFactory;
|
||||
@Autowired
|
||||
private IdGeneratorWrapper idGenerator;
|
||||
|
||||
/**
|
||||
@@ -111,9 +120,51 @@ public class FlowMessageServiceImpl extends BaseService<FlowMessage, Long> imple
|
||||
FlowTaskExt flowTaskExt = flowTaskExtService.getByProcessDefinitionIdAndTaskId(
|
||||
flowWorkOrder.getProcessDefinitionId(), task.getTaskDefinitionKey());
|
||||
if (flowTaskExt != null) {
|
||||
// 插入与当前消息关联任务的候选人
|
||||
this.saveMessageCandidateIdentityWithMessage(
|
||||
flowWorkOrder.getProcessInstanceId(), flowTaskExt, flowMessage.getMessageId());
|
||||
}
|
||||
// 插入与当前消息关联任务的指派人。
|
||||
if (StrUtil.isNotBlank(task.getAssignee())) {
|
||||
this.saveMessageCandidateIdentity(
|
||||
flowMessage.getMessageId(), FlowConstant.GROUP_TYPE_USER_VAR, task.getAssignee());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void saveNewCopyMessage(Task task, JSONObject copyDataJson) {
|
||||
ProcessInstance instance = flowApiService.getProcessInstance(task.getProcessInstanceId());
|
||||
BaseBusinessDataExtHelper helper = flowCustomExtFactory.getBusinessDataExtHelper();
|
||||
// 在线表单中,这个值为空。
|
||||
String businessShotData = helper.getBusinessData(
|
||||
instance.getProcessDefinitionKey(), instance.getProcessInstanceId(), instance.getBusinessKey());
|
||||
FlowMessage flowMessage = new FlowMessage();
|
||||
flowMessage.setMessageType(FlowMessageType.COPY_TYPE);
|
||||
flowMessage.setRemindCount(0);
|
||||
flowMessage.setProcessDefinitionId(instance.getProcessDefinitionId());
|
||||
flowMessage.setProcessDefinitionKey(instance.getProcessDefinitionKey());
|
||||
flowMessage.setProcessDefinitionName(instance.getProcessDefinitionName());
|
||||
flowMessage.setProcessInstanceId(instance.getProcessInstanceId());
|
||||
flowMessage.setProcessInstanceInitiator(instance.getStartUserId());
|
||||
flowMessage.setTaskId(task.getId());
|
||||
flowMessage.setTaskDefinitionKey(task.getTaskDefinitionKey());
|
||||
flowMessage.setTaskName(task.getName());
|
||||
flowMessage.setTaskStartTime(task.getCreateTime());
|
||||
flowMessage.setTaskAssignee(task.getAssignee());
|
||||
flowMessage.setTaskFinished(false);
|
||||
flowMessage.setBusinessDataShot(businessShotData);
|
||||
flowMessage.setOnlineFormData(businessShotData == null);
|
||||
// 如果是在线表单,这里就保存关联的在线表单Id,便于在线表单业务数据的查找。
|
||||
if (flowMessage.getOnlineFormData()) {
|
||||
TaskInfoVo taskInfo = JSON.parseObject(task.getFormKey(), TaskInfoVo.class);
|
||||
flowMessage.setBusinessDataShot(taskInfo.getFormId().toString());
|
||||
}
|
||||
this.saveNew(flowMessage);
|
||||
for (Map.Entry<String, Object> entries : copyDataJson.entrySet()) {
|
||||
this.saveMessageCandidateIdentityList(
|
||||
flowMessage.getMessageId(), entries.getKey(), entries.getValue().toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +190,59 @@ public class FlowMessageServiceImpl extends BaseService<FlowMessage, Long> imple
|
||||
|
||||
@Override
|
||||
public List<FlowMessage> getRemindingMessageListByUser() {
|
||||
return flowMessageMapper.getRemindingMessageListByUser(
|
||||
TokenData.takeFromRequest().getLoginName(), buildGroupIdSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FlowMessage> getCopyMessageListByUser(Boolean read) {
|
||||
return flowMessageMapper.getCopyMessageListByUser(
|
||||
TokenData.takeFromRequest().getLoginName(), buildGroupIdSet(), read);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCandidateIdentityOnMessage(Long messageId) {
|
||||
LambdaQueryWrapper<FlowMessageCandidateIdentity> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FlowMessageCandidateIdentity::getMessageId, messageId);
|
||||
queryWrapper.in(FlowMessageCandidateIdentity::getCandidateId, buildGroupIdSet());
|
||||
return flowMessageCandidateIdentityMapper.selectCount(queryWrapper) > 0;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void readCopyTask(Long messageId) {
|
||||
FlowMessageIdentityOperation operation = new FlowMessageIdentityOperation();
|
||||
operation.setId(idGenerator.nextLongId());
|
||||
operation.setMessageId(messageId);
|
||||
operation.setLoginName(TokenData.takeFromRequest().getLoginName());
|
||||
operation.setOperationType(FlowMessageOperationType.READ_FINISHED);
|
||||
operation.setOperationTime(new Date());
|
||||
flowMessageIdentityOperationMapper.insert(operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countRemindingMessageListByUser() {
|
||||
return flowMessageMapper.countRemindingMessageListByUser(
|
||||
TokenData.takeFromRequest().getLoginName(), buildGroupIdSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countCopyMessageByUser() {
|
||||
return flowMessageMapper.countCopyMessageListByUser(
|
||||
TokenData.takeFromRequest().getLoginName(), buildGroupIdSet());
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void removeByProcessInstanceId(String processInstanceId) {
|
||||
flowMessageCandidateIdentityMapper.deleteByProcessInstanceId(processInstanceId);
|
||||
flowMessageIdentityOperationMapper.deleteByProcessInstanceId(processInstanceId);
|
||||
LambdaQueryWrapper<FlowMessage> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FlowMessage::getProcessInstanceId, processInstanceId);
|
||||
flowMessageMapper.delete(queryWrapper);
|
||||
}
|
||||
|
||||
private Set<String> buildGroupIdSet() {
|
||||
TokenData tokenData = TokenData.takeFromRequest();
|
||||
Set<String> groupIdSet = new HashSet<>(1);
|
||||
groupIdSet.add(tokenData.getLoginName());
|
||||
@@ -148,7 +252,7 @@ public class FlowMessageServiceImpl extends BaseService<FlowMessage, Long> imple
|
||||
if (tokenData.getDeptId() != null) {
|
||||
groupIdSet.add(tokenData.getDeptId().toString());
|
||||
}
|
||||
return flowMessageMapper.getRemindingMessageListByUser(tokenData.getLoginName(), groupIdSet);
|
||||
return groupIdSet;
|
||||
}
|
||||
|
||||
private void parseAndAddIdArray(Set<String> groupIdSet, String idArray) {
|
||||
@@ -163,11 +267,11 @@ public class FlowMessageServiceImpl extends BaseService<FlowMessage, Long> imple
|
||||
private void saveMessageCandidateIdentityWithMessage(
|
||||
String processInstanceId, FlowTaskExt flowTaskExt, Long messageId) {
|
||||
this.saveMessageCandidateIdentityList(
|
||||
messageId, "username", flowTaskExt.getCandidateUsernames());
|
||||
messageId, FlowConstant.GROUP_TYPE_USER_VAR, flowTaskExt.getCandidateUsernames());
|
||||
this.saveMessageCandidateIdentityList(
|
||||
messageId, "role", flowTaskExt.getRoleIds());
|
||||
messageId, FlowConstant.GROUP_TYPE_ROLE_VAR, flowTaskExt.getRoleIds());
|
||||
this.saveMessageCandidateIdentityList(
|
||||
messageId, "dept", flowTaskExt.getDeptIds());
|
||||
messageId, FlowConstant.GROUP_TYPE_DEPT_VAR, flowTaskExt.getDeptIds());
|
||||
if (StrUtil.equals(flowTaskExt.getGroupType(), FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER)) {
|
||||
Object v = flowApiService.getProcessInstanceVariable(
|
||||
processInstanceId, FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER_VAR);
|
||||
@@ -227,7 +331,7 @@ public class FlowMessageServiceImpl extends BaseService<FlowMessage, Long> imple
|
||||
FlowMessageCandidateIdentity candidateIdentity = new FlowMessageCandidateIdentity();
|
||||
candidateIdentity.setId(idGenerator.nextLongId());
|
||||
candidateIdentity.setMessageId(messageId);
|
||||
candidateIdentity.setCandidateType(FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER_VAR);
|
||||
candidateIdentity.setCandidateType(candidateType);
|
||||
candidateIdentity.setCandidateId(candidateId);
|
||||
flowMessageCandidateIdentityMapper.insert(candidateIdentity);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ public class FlowTaskCommentServiceImpl extends BaseService<FlowTaskComment, Lon
|
||||
flowTaskComment.setId(idGenerator.nextLongId());
|
||||
TokenData tokenData = TokenData.takeFromRequest();
|
||||
flowTaskComment.setCreateUserId(tokenData.getUserId());
|
||||
flowTaskComment.setCreateLoginName(tokenData.getLoginName());
|
||||
flowTaskComment.setCreateUsername(tokenData.getShowName());
|
||||
flowTaskComment.setCreateTime(new Date());
|
||||
flowTaskCommentMapper.insert(flowTaskComment);
|
||||
@@ -69,8 +70,8 @@ public class FlowTaskCommentServiceImpl extends BaseService<FlowTaskComment, Lon
|
||||
*/
|
||||
@Override
|
||||
public List<FlowTaskComment> getFlowTaskCommentList(String processInstanceId) {
|
||||
LambdaQueryWrapper<FlowTaskComment> queryWrapper =
|
||||
new LambdaQueryWrapper<FlowTaskComment>().eq(FlowTaskComment::getProcessInstanceId, processInstanceId);
|
||||
LambdaQueryWrapper<FlowTaskComment> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FlowTaskComment::getProcessInstanceId, processInstanceId);
|
||||
queryWrapper.orderByAsc(FlowTaskComment::getId);
|
||||
return flowTaskCommentMapper.selectList(queryWrapper);
|
||||
}
|
||||
@@ -85,10 +86,29 @@ public class FlowTaskCommentServiceImpl extends BaseService<FlowTaskComment, Lon
|
||||
|
||||
@Override
|
||||
public FlowTaskComment getLatestFlowTaskComment(String processInstanceId) {
|
||||
LambdaQueryWrapper<FlowTaskComment> queryWrapper =
|
||||
new LambdaQueryWrapper<FlowTaskComment>().eq(FlowTaskComment::getProcessInstanceId, processInstanceId);
|
||||
LambdaQueryWrapper<FlowTaskComment> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FlowTaskComment::getProcessInstanceId, processInstanceId);
|
||||
queryWrapper.orderByDesc(FlowTaskComment::getId);
|
||||
IPage<FlowTaskComment> pageData = flowTaskCommentMapper.selectPage(new Page<>(1, 1), queryWrapper);
|
||||
return CollUtil.isEmpty(pageData.getRecords()) ? null : pageData.getRecords().get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlowTaskComment getLatestFlowTaskComment(String processInstanceId, String taskDefinitionKey) {
|
||||
LambdaQueryWrapper<FlowTaskComment> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FlowTaskComment::getProcessInstanceId, processInstanceId);
|
||||
queryWrapper.eq(FlowTaskComment::getTaskKey, taskDefinitionKey);
|
||||
queryWrapper.orderByDesc(FlowTaskComment::getId);
|
||||
IPage<FlowTaskComment> pageData = flowTaskCommentMapper.selectPage(new Page<>(1, 1), queryWrapper);
|
||||
return CollUtil.isEmpty(pageData.getRecords()) ? null : pageData.getRecords().get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlowTaskComment getFirstFlowTaskComment(String processInstanceId) {
|
||||
LambdaQueryWrapper<FlowTaskComment> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FlowTaskComment::getProcessInstanceId, processInstanceId);
|
||||
queryWrapper.orderByAsc(FlowTaskComment::getId);
|
||||
IPage<FlowTaskComment> pageData = flowTaskCommentMapper.selectPage(new Page<>(1, 1), queryWrapper);
|
||||
return CollUtil.isEmpty(pageData.getRecords()) ? null : pageData.getRecords().get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package com.orangeforms.common.flow.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
|
||||
import com.orangeforms.common.core.constant.GlobalDeletedFlag;
|
||||
import com.orangeforms.common.core.object.GlobalThreadLocal;
|
||||
import com.orangeforms.common.core.object.MyRelationParam;
|
||||
import com.orangeforms.common.core.object.TokenData;
|
||||
import com.orangeforms.common.core.base.service.BaseService;
|
||||
@@ -11,6 +14,9 @@ import com.orangeforms.common.flow.constant.FlowTaskStatus;
|
||||
import com.orangeforms.common.flow.dao.FlowWorkOrderMapper;
|
||||
import com.orangeforms.common.flow.model.FlowWorkOrder;
|
||||
import com.orangeforms.common.flow.service.FlowWorkOrderService;
|
||||
import com.orangeforms.common.flow.util.BaseFlowIdentityExtHelper;
|
||||
import com.orangeforms.common.flow.util.FlowCustomExtFactory;
|
||||
import com.orangeforms.common.flow.vo.FlowWorkOrderVo;
|
||||
import com.orangeforms.common.sequence.wrapper.IdGeneratorWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
@@ -19,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 工作流工单表数据操作服务类。
|
||||
@@ -34,6 +41,8 @@ public class FlowWorkOrderServiceImpl extends BaseService<FlowWorkOrder, Long> i
|
||||
private FlowWorkOrderMapper flowWorkOrderMapper;
|
||||
@Autowired
|
||||
private IdGeneratorWrapper idGenerator;
|
||||
@Autowired
|
||||
private FlowCustomExtFactory flowCustomExtFactory;
|
||||
|
||||
/**
|
||||
* 返回当前Service的主表Mapper对象。
|
||||
@@ -144,4 +153,36 @@ public class FlowWorkOrderServiceImpl extends BaseService<FlowWorkOrder, Long> i
|
||||
queryWrapper.eq(FlowWorkOrder::getProcessInstanceId, processInstanceId);
|
||||
flowWorkOrderMapper.update(flowWorkOrder, queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDataPermOnFlowWorkOrder(String processInstanceId) {
|
||||
// 开启数据权限,并进行验证。
|
||||
boolean originalFlag = GlobalThreadLocal.setDataFilter(true);
|
||||
int count;
|
||||
try {
|
||||
FlowWorkOrder filter = new FlowWorkOrder();
|
||||
filter.setProcessInstanceId(processInstanceId);
|
||||
count = flowWorkOrderMapper.selectCount(new QueryWrapper<>(filter));
|
||||
} finally {
|
||||
// 恢复之前的数据权限标记
|
||||
GlobalThreadLocal.setDataFilter(originalFlag);
|
||||
}
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillUserShowNameByLoginName(List<FlowWorkOrderVo> dataList) {
|
||||
BaseFlowIdentityExtHelper identityExtHelper = flowCustomExtFactory.getFlowIdentityExtHelper();
|
||||
Set<String> loginNameSet = dataList.stream()
|
||||
.map(FlowWorkOrderVo::getSubmitUsername).collect(Collectors.toSet());
|
||||
if (CollUtil.isEmpty(loginNameSet)) {
|
||||
return;
|
||||
}
|
||||
Map<String, String> userNameMap = identityExtHelper.mapUserShowNameByLoginName(loginNameSet);
|
||||
dataList.forEach(workOrder -> {
|
||||
if (StrUtil.isNotBlank(workOrder.getSubmitUsername())) {
|
||||
workOrder.setUserShowName(userNameMap.get(workOrder.getSubmitUsername()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,16 +9,13 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 工作流实例执行结束之后,需要需要做业务表的数据同步,可实现该接口。
|
||||
* 该插件通常用于一张表单多次提交的场景。为了避免修改后的审批中数据,由于尚未通过审批,而此时依赖该业务作为关联表时,
|
||||
* 关联到的数据往往是尚未通过审批的脏数据,因此需要做审批表和发布表的数据隔离。仅当审批流程完全结束且通过审批后,在
|
||||
* 将审批表及其一对一、一对多、多对多关联表中的数据,同步到发布表及其关联表中。至于具体需要同步那些表数据,需按需求而定。
|
||||
* 工作流业务数据扩展帮助实现类。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2021-06-06
|
||||
*/
|
||||
@Slf4j
|
||||
public class BaseDataSyncExtHelper {
|
||||
public class BaseBusinessDataExtHelper {
|
||||
|
||||
private Map<String, BaseFlowService> serviceMap = new HashMap<>();
|
||||
|
||||
@@ -44,16 +41,29 @@ public class BaseDataSyncExtHelper {
|
||||
*/
|
||||
public void triggerSync(String processDefinitionKey, String processInstanceId, String businessKey) {
|
||||
BaseFlowService service = serviceMap.get(processDefinitionKey);
|
||||
if (service != null) {
|
||||
if (service != null && service.supportSyncBusinessData()) {
|
||||
try {
|
||||
service.doSyncBusinessData(processInstanceId, businessKey);
|
||||
service.syncBusinessData(processInstanceId, businessKey);
|
||||
} catch (Exception e) {
|
||||
String errorMessage = String.format(
|
||||
"Failed to call doSyncBusinessData with processDefinitionKey {%s}, businessKey {%s}",
|
||||
"Failed to call syncBusinessData with processDefinitionKey {%s}, businessKey {%s}",
|
||||
processDefinitionKey, businessKey);
|
||||
log.error(errorMessage, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取详细的业务数据,包括主表、一对一、一对多、多对多从表及其字典数据。
|
||||
*
|
||||
* @param processDefinitionKey 流程定义标识。
|
||||
* @param processInstanceId 流程实例Id。
|
||||
* @param businessKey 业务主表的主键Id。
|
||||
* @return JSON格式化后的业务数据。
|
||||
*/
|
||||
public String getBusinessData(String processDefinitionKey, String processInstanceId, String businessKey) {
|
||||
BaseFlowService service = serviceMap.get(processDefinitionKey);
|
||||
return service == null ? null : service.getBusinessData(processInstanceId, businessKey);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.orangeforms.common.flow.listener.DeptPostLeaderListener;
|
||||
import com.orangeforms.common.flow.listener.UpDeptPostLeaderListener;
|
||||
import org.flowable.engine.delegate.TaskListener;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -140,6 +141,25 @@ public interface BaseFlowIdentityExtHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前服务是否支持数据权限。
|
||||
*
|
||||
* @return true表示支持,否则false。
|
||||
*/
|
||||
default Boolean supprtDataPerm() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 映射用户的登录名到用户的显示名。
|
||||
*
|
||||
* @param loginNameSet 用户登录名集合。
|
||||
* @return 用户登录名和显示名的Map,key为登录名,value是显示名。
|
||||
*/
|
||||
default Map<String, String> mapUserShowNameByLoginName(Set<String> loginNameSet) {
|
||||
return new HashMap<>(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务执行人是当前部门领导岗位的任务监听器。
|
||||
* 通常会在没有找到领导部门岗位Id的时候,为当前任务指定其他的指派人、候选人或候选组。
|
||||
|
||||
@@ -13,7 +13,7 @@ public class FlowCustomExtFactory {
|
||||
|
||||
private BaseFlowIdentityExtHelper flowIdentityExtHelper;
|
||||
|
||||
private BaseDataSyncExtHelper dataSyncExtHelper = new BaseDataSyncExtHelper();
|
||||
private BaseBusinessDataExtHelper businessDataExtHelper = new BaseBusinessDataExtHelper();
|
||||
|
||||
/**
|
||||
* 获取业务模块自行实现的用户身份相关的扩展帮助实现类。
|
||||
@@ -34,11 +34,11 @@ public class FlowCustomExtFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程结束后数据同步的帮助实现类。
|
||||
* 获取有关业务数据的扩展帮助实现类。
|
||||
*
|
||||
* @return 流程结束后数据同步的帮助实现类。
|
||||
* @return 有关业务数据的扩展帮助实现类。
|
||||
*/
|
||||
public BaseDataSyncExtHelper getDataSyncExtHelper() {
|
||||
return dataSyncExtHelper;
|
||||
public BaseBusinessDataExtHelper getBusinessDataExtHelper() {
|
||||
return businessDataExtHelper;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.orangeforms.common.flow.model.FlowWorkOrder;
|
||||
import com.orangeforms.common.flow.model.constant.FlowEntryStatus;
|
||||
import com.orangeforms.common.flow.service.FlowApiService;
|
||||
import com.orangeforms.common.flow.service.FlowEntryService;
|
||||
import com.orangeforms.common.flow.service.FlowWorkOrderService;
|
||||
import com.orangeforms.common.flow.vo.FlowWorkOrderVo;
|
||||
import com.orangeforms.common.flow.vo.TaskInfoVo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -53,6 +54,10 @@ public class FlowOperationHelper {
|
||||
private FlowEntryService flowEntryService;
|
||||
@Autowired
|
||||
private FlowApiService flowApiService;
|
||||
@Autowired
|
||||
private FlowWorkOrderService flowWorkOrderService;
|
||||
@Autowired
|
||||
private FlowCustomExtFactory flowCustomExtFactory;
|
||||
|
||||
/**
|
||||
* 验证并获取流程对象。
|
||||
@@ -157,8 +162,10 @@ public class FlowOperationHelper {
|
||||
String loginName = TokenData.takeFromRequest().getLoginName();
|
||||
if (StrUtil.isBlank(taskId)) {
|
||||
if (!StrUtil.equals(loginName, instance.getStartUserId())) {
|
||||
errorMessage = "数据验证失败,指定历史流程的发起人与当前用户不匹配!";
|
||||
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||
if (!flowWorkOrderService.hasDataPermOnFlowWorkOrder(processInstanceId)) {
|
||||
errorMessage = "数据验证失败,指定历史流程的发起人与当前用户不匹配,或者没有查看该工单详情的数据权限!";
|
||||
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
HistoricTaskInstance taskInstance = flowApiService.getHistoricTaskInstance(processInstanceId, taskId);
|
||||
@@ -167,8 +174,10 @@ public class FlowOperationHelper {
|
||||
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||
}
|
||||
if (!StrUtil.equals(loginName, taskInstance.getAssignee())) {
|
||||
errorMessage = "数据验证失败,历史任务的指派人与当前用户不匹配!";
|
||||
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||
if (!flowWorkOrderService.hasDataPermOnFlowWorkOrder(processInstanceId)) {
|
||||
errorMessage = "数据验证失败,历史任务的指派人与当前用户不匹配,或者没有查看该工单详情的数据权限!";
|
||||
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ResponseResult.success(instance);
|
||||
@@ -269,7 +278,13 @@ public class FlowOperationHelper {
|
||||
filter = new FlowWorkOrder();
|
||||
}
|
||||
filter.setProcessDefinitionKey(processDefinitionKey);
|
||||
filter.setCreateUserId(TokenData.takeFromRequest().getUserId());
|
||||
// 下面的方法会帮助构建工单的数据权限过滤条件,和业务希望相比,如果当前系统没有支持数据权限,
|
||||
// 用户则只能看到自己发起的工单,否则按照数据权限过滤。然而需要特殊处理的是,如果用户的数据
|
||||
// 权限中,没有包含能看自己,这里也需要自动给加上。
|
||||
BaseFlowIdentityExtHelper flowIdentityExtHelper = flowCustomExtFactory.getFlowIdentityExtHelper();
|
||||
if (!flowIdentityExtHelper.supprtDataPerm()) {
|
||||
filter.setCreateUserId(TokenData.takeFromRequest().getUserId());
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ public class FlowEntryVo {
|
||||
* 最新发布时间。
|
||||
*/
|
||||
@ApiModelProperty(value = "最新发布时间")
|
||||
private Date lastestPublishTime;
|
||||
private Date latestPublishTime;
|
||||
|
||||
/**
|
||||
* 流程状态。
|
||||
|
||||
@@ -64,12 +64,24 @@ public class FlowTaskCommentVo {
|
||||
@ApiModelProperty(value = "委托指定人,比如加签、转办等")
|
||||
private String delegateAssginee;
|
||||
|
||||
/**
|
||||
* 自定义数据。开发者可自行扩展,推荐使用JSON格式数据。
|
||||
*/
|
||||
@ApiModelProperty(value = "自定义数据s")
|
||||
private String customBusinessData;
|
||||
|
||||
/**
|
||||
* 创建者Id。
|
||||
*/
|
||||
@ApiModelProperty(value = "创建者Id")
|
||||
private Long createUserId;
|
||||
|
||||
/**
|
||||
* 创建者登录名。
|
||||
*/
|
||||
@ApiModelProperty(value = "创建者登录名")
|
||||
private String createLoginName;
|
||||
|
||||
/**
|
||||
* 创建者显示名。
|
||||
*/
|
||||
|
||||
@@ -108,6 +108,12 @@ public class FlowWorkOrderVo {
|
||||
@ApiModelProperty(value = "flowStatus 常量字典关联数据")
|
||||
private Map<String, Object> flowStatusDictMap;
|
||||
|
||||
/**
|
||||
* 用户的显示名。
|
||||
*/
|
||||
@ApiModelProperty(value = "用户的显示名")
|
||||
private String userShowName;
|
||||
|
||||
/**
|
||||
* FlowEntryPublish对象中的同名字段。
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user