commit:升级到vue3,更新最近工作流技术栈,支持sa-token

This commit is contained in:
Jerry
2024-07-05 22:42:33 +08:00
parent bbcc608584
commit 565ecb6371
1751 changed files with 236790 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
package com.orangeforms.common.flow.advice;
import com.alibaba.fastjson.JSON;
import com.orangeforms.common.core.constant.ErrorCodeEnum;
import com.orangeforms.common.core.object.ResponseResult;
import com.orangeforms.common.core.util.ContextUtil;
import com.orangeforms.common.flow.exception.FlowEmptyUserException;
import com.orangeforms.common.flow.model.FlowTaskComment;
import com.orangeforms.common.flow.service.FlowTaskCommentService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.FlowableException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* 流程业务层的异常处理类。
*
* @author Jerry
* @date 2024-07-02
*/
@Slf4j
@Order(1)
@RestControllerAdvice("com.orangeforms")
public class FlowExceptionHandler {
@Autowired
private FlowTaskCommentService flowTaskCommentService;
@ExceptionHandler(value = FlowableException.class)
public ResponseResult<Void> exceptionHandle(FlowableException ex, HttpServletRequest request) {
if (ex instanceof FlowEmptyUserException) {
FlowEmptyUserException flowEmptyUserException = (FlowEmptyUserException) ex;
FlowTaskComment comment = JSON.parseObject(flowEmptyUserException.getMessage(), FlowTaskComment.class);
flowTaskCommentService.saveNew(comment);
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, "下一个任务节点的审批人为空,提交被自动驳回!");
}
log.error("Unhandled FlowException from URL [" + request.getRequestURI() + "]", ex);
ContextUtil.getHttpResponse().setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return ResponseResult.error(ErrorCodeEnum.UNHANDLED_EXCEPTION, ex.getMessage());
}
@SuppressWarnings("unchecked")
private <T extends Exception> T findCause(Throwable ex, Class<T> clazz) {
if (ex.getCause() == null) {
return null;
}
if (ex.getCause().getClass().equals(clazz)) {
return (T) ex.getCause();
} else {
return this.findCause(ex.getCause(), clazz);
}
}
}

View File

@@ -0,0 +1,26 @@
package com.orangeforms.common.flow.base.service;
import com.orangeforms.common.flow.model.FlowWorkOrder;
/**
* 工作流在线表单的服务接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface BaseFlowOnlineService {
/**
* 更新在线表单主表数据的流程状态字段值。
*
* @param workOrder 工单对象。
*/
void updateFlowStatus(FlowWorkOrder workOrder);
/**
* 根据工单对象级联删除业务数据。
*
* @param workOrder 工单对象。
*/
void deleteBusinessData(FlowWorkOrder workOrder);
}

View File

@@ -0,0 +1,48 @@
package com.orangeforms.common.flow.config;
import com.orangeforms.common.core.config.DynamicDataSource;
import com.orangeforms.common.core.constant.ApplicationConstant;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.impl.AbstractEngineConfiguration;
import org.flowable.common.engine.impl.EngineConfigurator;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import javax.sql.DataSource;
import java.util.Map;
/**
* 服务启动过程中动态切换flowable引擎内置表所在的数据源。
*
* @author Jerry
* @date 2024-07-02
*/
@Slf4j
public class CustomEngineConfigurator implements EngineConfigurator {
@Override
public void beforeInit(AbstractEngineConfiguration engineConfiguration) {
DataSource dataSource = engineConfiguration.getDataSource();
if (dataSource instanceof TransactionAwareDataSourceProxy) {
TransactionAwareDataSourceProxy proxy = (TransactionAwareDataSourceProxy) dataSource;
DataSource targetDataSource = proxy.getTargetDataSource();
if (targetDataSource instanceof DynamicDataSource) {
DynamicDataSource dynamicDataSource = (DynamicDataSource) targetDataSource;
Map<Object, DataSource> dynamicDataSourceMap = dynamicDataSource.getResolvedDataSources();
DataSource flowDataSource = dynamicDataSourceMap.get(ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE);
if (flowDataSource != null) {
engineConfiguration.setDataSource(flowDataSource);
}
}
}
}
@Override
public void configure(AbstractEngineConfiguration engineConfiguration) {
// 默认实现。
}
@Override
public int getPriority() {
return 0;
}
}

View File

@@ -0,0 +1,13 @@
package com.orangeforms.common.flow.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
/**
* common-flow模块的自动配置引导类。
*
* @author Jerry
* @date 2024-07-02
*/
@EnableConfigurationProperties({FlowProperties.class})
public class FlowAutoConfig {
}

View File

@@ -0,0 +1,20 @@
package com.orangeforms.common.flow.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 工作流的配置对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
@ConfigurationProperties(prefix = "common-flow")
public class FlowProperties {
/**
* 工作落工单操作接口的URL前缀。
*/
private String urlPrefix;
}

View File

@@ -0,0 +1,97 @@
package com.orangeforms.common.flow.constant;
/**
* 工作流任务触发BUTTON。
*
* @author Jerry
* @date 2024-07-02
*/
public final class FlowApprovalType {
/**
* 保存。
*/
public static final String SAVE = "save";
/**
* 同意。
*/
public static final String AGREE = "agree";
/**
* 拒绝。
*/
public static final String REFUSE = "refuse";
/**
* 驳回。
*/
public static final String REJECT = "reject";
/**
* 撤销。
*/
public static final String REVOKE = "revoke";
/**
* 指派。
*/
public static final String TRANSFER = "transfer";
/**
* 多实例会签。
*/
public static final String MULTI_SIGN = "multi_sign";
/**
* 会签同意。
*/
public static final String MULTI_AGREE = "multi_agree";
/**
* 会签拒绝。
*/
public static final String MULTI_REFUSE = "multi_refuse";
/**
* 会签弃权。
*/
public static final String MULTI_ABSTAIN = "multi_abstain";
/**
* 多实例加签。
*/
public static final String MULTI_CONSIGN = "multi_consign";
/**
* 多实例减签。
*/
public static final String MULTI_MINUS_SIGN = "multi_minus_sign";
/**
* 中止。
*/
public static final String STOP = "stop";
/**
* 干预。
*/
public static final String INTERVENE = "intervene";
/**
* 自由跳转。
*/
public static final String FREE_JUMP = "free_jump";
/**
* 流程复活。
*/
public static final String REUSED = "reused";
/**
* 流程复活。
*/
public static final String REVIVE = "revive";
/**
* 超时自动审批。
*/
public static final String TIMEOUT_AUTO_COMPLETE = "timeout_auto_complete";
/**
* 空审批人自动审批。
*/
public static final String EMPTY_USER_AUTO_COMPLETE = "empty_user_auto_complete";
/**
* 空审批人自动退回。
*/
public static final String EMPTY_USER_AUTO_REJECT = "empty_user_auto_reject";
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private FlowApprovalType() {
}
}

View File

@@ -0,0 +1,25 @@
package com.orangeforms.common.flow.constant;
/**
* 待办任务回退类型。
*
* @author Jerry
* @date 2024-07-02
*/
public final class FlowBackType {
/**
* 驳回。
*/
public static final int REJECT = 0;
/**
* 撤回。
*/
public static final int REVOKE = 1;
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private FlowBackType() {
}
}

View File

@@ -0,0 +1,33 @@
package com.orangeforms.common.flow.constant;
/**
* 内置的流程审批状态。
*
* @author Jerry
* @date 2024-07-02
*/
public class FlowBuiltinApprovalStatus {
/**
* 同意。
*/
public static final int AGREED = 1;
/**
* 拒绝。
*/
public static final int REFUSED = 2;
/**
* 会签同意。
*/
public static final int MULTI_AGREED = 3;
/**
* 会签拒绝。
*/
public static final int MULTI_REFUSED = 4;
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private FlowBuiltinApprovalStatus() {
}
}

View File

@@ -0,0 +1,266 @@
package com.orangeforms.common.flow.constant;
/**
* 工作流中的常量数据。
*
* @author Jerry
* @date 2024-07-02
*/
public class FlowConstant {
/**
* 标识流程实例启动用户的变量名。
*/
public static final String START_USER_NAME_VAR = "${startUserName}";
/**
* 流程实例发起人变量名。
*/
public static final String PROC_INSTANCE_INITIATOR_VAR = "initiator";
/**
* 流程实例中发起人用户的变量名。
*/
public static final String PROC_INSTANCE_START_USER_NAME_VAR = "startUserName";
/**
* 流程任务的指定人变量。
*/
public static final String TASK_APPOINTED_ASSIGNEE_VAR = "appointedAssignee";
/**
* 操作类型变量。
*/
public static final String OPERATION_TYPE_VAR = "operationType";
/**
* 提交用户。
*/
public static final String SUBMIT_USER_VAR = "submitUser";
/**
* 多任务拒绝数量变量。
*/
public static final String MULTI_REFUSE_COUNT_VAR = "multiRefuseCount";
/**
* 多任务同意数量变量。
*/
public static final String MULTI_AGREE_COUNT_VAR = "multiAgreeCount";
/**
* 多任务弃权数量变量。
*/
public static final String MULTI_ABSTAIN_COUNT_VAR = "multiAbstainCount";
/**
* 会签发起任务。
*/
public static final String MULTI_SIGN_START_TASK_VAR = "multiSignStartTask";
/**
* 会签任务总数量。
*/
public static final String MULTI_SIGN_NUM_OF_INSTANCES_VAR = "multiNumOfInstances";
/**
* 会签任务执行的批次Id。
*/
public static final String MULTI_SIGN_TASK_EXECUTION_ID_VAR = "taskExecutionId";
/**
* 多实例实例数量变量。
*/
public static final String NUMBER_OF_INSTANCES_VAR = "nrOfInstances";
/**
* 多实例已完成实例数量变量。
*/
public static final String NUMBER_OF_COMPLETED_INSTANCES_VAR = "nrOfCompletedInstances";
/**
* 多任务指派人列表变量。
*/
public static final String MULTI_ASSIGNEE_LIST_VAR = "assigneeList";
/**
* 上级部门领导审批变量。
*/
public static final String GROUP_TYPE_UP_DEPT_POST_LEADER_VAR = "upDeptPostLeader";
/**
* 本部门领导审批变量。
*/
public static final String GROUP_TYPE_DEPT_POST_LEADER_VAR = "deptPostLeader";
/**
* 所有部门岗位审批变量。
*/
public static final String GROUP_TYPE_ALL_DEPT_POST_VAR = "allDeptPost";
/**
* 本部门岗位审批变量。
*/
public static final String GROUP_TYPE_SELF_DEPT_POST_VAR = "selfDeptPost";
/**
* 同级部门岗位审批变量。
*/
public static final String GROUP_TYPE_SIBLING_DEPT_POST_VAR = "siblingDeptPost";
/**
* 上级部门岗位审批变量。
*/
public static final String GROUP_TYPE_UP_DEPT_POST_VAR = "upDeptPost";
/**
* 任意部门关联的岗位审批变量。
*/
public static final String GROUP_TYPE_DEPT_POST_VAR = "deptPost";
/**
* 指定角色分组变量。
*/
public static final String GROUP_TYPE_ROLE_VAR = "role";
/**
* 指定部门分组变量。
*/
public static final String GROUP_TYPE_DEPT_VAR = "dept";
/**
* 指定用户分组变量。
*/
public static final String GROUP_TYPE_USER_VAR = "user";
/**
* 指定审批人。
*/
public static final String GROUP_TYPE_ASSIGNEE = "ASSIGNEE";
/**
* 岗位。
*/
public static final String GROUP_TYPE_POST = "POST";
/**
* 上级部门领导审批。
*/
public static final String GROUP_TYPE_UP_DEPT_POST_LEADER = "UP_DEPT_POST_LEADER";
/**
* 本部门岗位领导审批。
*/
public static final String GROUP_TYPE_DEPT_POST_LEADER = "DEPT_POST_LEADER";
/**
* 本部门岗位前缀。
*/
public static final String SELF_DEPT_POST_PREFIX = "SELF_DEPT_";
/**
* 上级部门岗位前缀。
*/
public static final String UP_DEPT_POST_PREFIX = "UP_DEPT_";
/**
* 同级部门岗位前缀。
*/
public static final String SIBLING_DEPT_POST_PREFIX = "SIBLING_DEPT_";
/**
* 当前流程实例所有任务的抄送数据前缀。
*/
public static final String COPY_DATA_MAP_PREFIX = "copyDataMap_";
/**
* 作为临时变量存入任务变量JSONObject对象时的key。
*/
public static final String COPY_DATA_KEY = "copyDataKey";
/**
* 流程中业务快照数据中主表数据的Key。
*/
public static final String MASTER_DATA_KEY = "masterData";
/**
* 流程中业务快照数据中关联从表数据的Key。
*/
public static final String SLAVE_DATA_KEY = "slaveData";
/**
* 流程任务的最近更新状态的Key。
*/
public static final String LATEST_APPROVAL_STATUS_KEY = "latestApprovalStatus";
/**
* 流程用户任务待办之前的通知类型的Key。
*/
public static final String USER_TASK_NOTIFY_TYPES_KEY = "flowNotifyTypeList";
/**
* 流程用户任务自动跳过类型的Key。
*/
public static final String USER_TASK_AUTO_SKIP_KEY = "autoSkipType";
/**
* 流程用户任务驳回类型的Key。
*/
public static final String USER_TASK_REJECT_TYPE_KEY = "rejectType";
/**
* 驳回时携带的变量数据。
*/
public static final String REJECT_TO_SOURCE_DATA_VAR = "rejectData";
/**
* 驳回时携带的变量数据。
*/
public static final String REJECT_BACK_TO_SOURCE_DATA_VAR = "rejectBackData";
/**
* 指定审批人。
*/
public static final String DELEGATE_ASSIGNEE_VAR = "defaultAssignee";
/**
* 业务主表对象的键。目前仅仅用户在线表单工作流。
*/
public static final String MASTER_TABLE_KEY = "masterTable";
/**
* 不删除任务超时作业。
*/
public static final String NOT_DELETE_TIMEOUT_TASK_JOB_KEY = "notDeleteTimeoutTaskJob";
/**
* 用户任务超时小时数。
*/
public static final String TASK_TIMEOUT_HOURS = "timeoutHours";
/**
* 用户任务超时处理方式。
*/
public static final String TASK_TIMEOUT_HANDLE_WAY = "timeoutHandleWay";
/**
* 用户任务超时指定审批人。
*/
public static final String TASK_TIMEOUT_DEFAULT_ASSIGNEE = "defaultAssignee";
/**
* 空处理人处理方式。
*/
public static final String EMPTY_USER_HANDLE_WAY = "emptyUserHandleWay";
/**
* 空处理人时指定的审批人。
*/
public static final String EMPTY_USER_TO_ASSIGNEE = "emptyUserToAssignee";
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private FlowConstant() {
}
}

View File

@@ -0,0 +1,49 @@
package com.orangeforms.common.flow.constant;
/**
* 工作流任务类型。
*
* @author Jerry
* @date 2024-07-02
*/
public final class FlowTaskStatus {
/**
* 已提交。
*/
public static final int SUBMITTED = 0;
/**
* 审批中。
*/
public static final int APPROVING = 1;
/**
* 被拒绝。
*/
public static final int REFUSED = 2;
/**
* 已结束。
*/
public static final int FINISHED = 3;
/**
* 提前停止。
*/
public static final Integer STOPPED = 4;
/**
* 已取消。
*/
public static final Integer CANCELLED = 5;
/**
* 保存草稿。
*/
public static final Integer DRAFT = 6;
/**
* 流程复活。
*/
public static final Integer REVIVE = 7;
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private FlowTaskStatus() {
}
}

View File

@@ -0,0 +1,25 @@
package com.orangeforms.common.flow.constant;
/**
* 工作流任务类型。
*
* @author Jerry
* @date 2024-07-02
*/
public final class FlowTaskType {
/**
* 其他类型任务。
*/
public static final int OTHER_TYPE = 0;
/**
* 用户任务。
*/
public static final int USER_TYPE = 1;
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private FlowTaskType() {
}
}

View File

@@ -0,0 +1,232 @@
package com.orangeforms.common.flow.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.tags.Tag;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.page.PageMethod;
import com.orangeforms.common.core.annotation.MyRequestBody;
import com.orangeforms.common.core.constant.ErrorCodeEnum;
import com.orangeforms.common.core.object.*;
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.log.annotation.OperationLog;
import com.orangeforms.common.log.model.constant.SysOperationLogType;
import com.orangeforms.common.flow.dto.*;
import com.orangeforms.common.flow.model.*;
import com.orangeforms.common.flow.model.constant.FlowEntryStatus;
import com.orangeforms.common.flow.service.*;
import com.orangeforms.common.flow.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.groups.Default;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
/**
* 工作流流程分类接口。
*
* @author Jerry
* @date 2024-07-02
*/
@Tag(name = "工作流流程分类接口")
@Slf4j
@RestController
@RequestMapping("${common-flow.urlPrefix}/flowCategory")
@ConditionalOnProperty(name = "common-flow.operationEnabled", havingValue = "true")
public class FlowCategoryController {
@Autowired
private FlowCategoryService flowCategoryService;
@Autowired
private FlowEntryService flowEntryService;
/**
* 新增FlowCategory数据。
*
* @param flowCategoryDto 新增对象。
* @return 应答结果对象包含新增对象主键Id。
*/
@ApiOperationSupport(ignoreParameters = {"flowCategoryDto.categoryId"})
@SaCheckPermission("flowCategory.all")
@OperationLog(type = SysOperationLogType.ADD)
@PostMapping("/add")
public ResponseResult<Long> add(@MyRequestBody FlowCategoryDto flowCategoryDto) {
String errorMessage = MyCommonUtil.getModelValidationError(flowCategoryDto);
if (errorMessage != null) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
FlowCategory flowCategory = MyModelUtil.copyTo(flowCategoryDto, FlowCategory.class);
if (flowCategoryService.existByCode(flowCategory.getCode())) {
return ResponseResult.error(ErrorCodeEnum.DUPLICATED_UNIQUE_KEY, "数据验证失败,当前流程分类已经存在!");
}
flowCategory = flowCategoryService.saveNew(flowCategory);
return ResponseResult.success(flowCategory.getCategoryId());
}
/**
* 更新FlowCategory数据。
*
* @param flowCategoryDto 更新对象。
* @return 应答结果对象。
*/
@SaCheckPermission("flowCategory.all")
@OperationLog(type = SysOperationLogType.UPDATE)
@PostMapping("/update")
public ResponseResult<Void> update(@MyRequestBody FlowCategoryDto flowCategoryDto) {
String errorMessage = MyCommonUtil.getModelValidationError(flowCategoryDto, Default.class, UpdateGroup.class);
if (errorMessage != null) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
FlowCategory flowCategory = MyModelUtil.copyTo(flowCategoryDto, FlowCategory.class);
ResponseResult<FlowCategory> verifyResult = this.doVerifyAndGet(flowCategory.getCategoryId());
if (!verifyResult.isSuccess()) {
return ResponseResult.errorFrom(verifyResult);
}
FlowCategory originalFlowCategory = verifyResult.getData();
if (!StrUtil.equals(flowCategory.getCode(), originalFlowCategory.getCode())) {
FlowEntry filter = new FlowEntry();
filter.setCategoryId(flowCategory.getCategoryId());
filter.setStatus(FlowEntryStatus.PUBLISHED);
List<FlowEntry> flowEntryList = flowEntryService.getListByFilter(filter);
if (CollUtil.isNotEmpty(flowEntryList)) {
errorMessage = "数据验证失败,当前流程分类存在已经发布的流程数据,因此分类标识不能修改!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
if (flowCategoryService.existByCode(flowCategory.getCode())) {
errorMessage = "数据验证失败,当前流程分类已经存在!";
return ResponseResult.error(ErrorCodeEnum.DUPLICATED_UNIQUE_KEY, errorMessage);
}
}
if (!flowCategoryService.update(flowCategory, originalFlowCategory)) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
}
return ResponseResult.success();
}
/**
* 删除FlowCategory数据。
*
* @param categoryId 删除对象主键Id。
* @return 应答结果对象。
*/
@SaCheckPermission("flowCategory.all")
@OperationLog(type = SysOperationLogType.DELETE)
@PostMapping("/delete")
public ResponseResult<Void> delete(@MyRequestBody Long categoryId) {
String errorMessage;
ResponseResult<FlowCategory> verifyResult = this.doVerifyAndGet(categoryId);
if (!verifyResult.isSuccess()) {
return ResponseResult.errorFrom(verifyResult);
}
FlowEntry filter = new FlowEntry();
filter.setCategoryId(categoryId);
List<FlowEntry> flowEntryList = flowEntryService.getListByFilter(filter);
if (CollUtil.isNotEmpty(flowEntryList)) {
errorMessage = "数据验证失败,请先删除当前流程分类关联的流程数据!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
if (!flowCategoryService.remove(categoryId)) {
errorMessage = "数据操作失败,删除的对象不存在,请刷新后重试!";
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
}
return ResponseResult.success();
}
/**
* 列出符合过滤条件的FlowCategory列表。
*
* @param flowCategoryDtoFilter 过滤对象。
* @param orderParam 排序参数。
* @param pageParam 分页参数。
* @return 应答结果对象,包含查询结果集。
*/
@SaCheckPermission("flowCategory.all")
@PostMapping("/list")
public ResponseResult<MyPageData<FlowCategoryVo>> list(
@MyRequestBody FlowCategoryDto flowCategoryDtoFilter,
@MyRequestBody MyOrderParam orderParam,
@MyRequestBody MyPageParam pageParam) {
if (pageParam != null) {
PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize());
}
FlowCategory flowCategoryFilter = MyModelUtil.copyTo(flowCategoryDtoFilter, FlowCategory.class);
String orderBy = MyOrderParam.buildOrderBy(orderParam, FlowCategory.class);
List<FlowCategory> flowCategoryList = flowCategoryService.getFlowCategoryListWithRelation(flowCategoryFilter, orderBy);
return ResponseResult.success(MyPageUtil.makeResponseData(flowCategoryList, FlowCategoryVo.class));
}
/**
* 查看指定FlowCategory对象详情。
*
* @param categoryId 指定对象主键Id。
* @return 应答结果对象,包含对象详情。
*/
@SaCheckPermission("flowCategory.all")
@GetMapping("/view")
public ResponseResult<FlowCategoryVo> view(@RequestParam Long categoryId) {
ResponseResult<FlowCategory> verifyResult = this.doVerifyAndGet(categoryId);
if (!verifyResult.isSuccess()) {
return ResponseResult.errorFrom(verifyResult);
}
return ResponseResult.success(verifyResult.getData(), FlowCategoryVo.class);
}
/**
* 以字典形式返回全部FlowCategory数据集合。字典的键值为[categoryId, name]。
* 白名单接口,登录用户均可访问。
*
* @param filter 过滤对象。
* @return 应答结果对象,包含的数据为 List<Map<String, String>>map中包含两条记录key的值分别是id和namevalue对应具体数据。
*/
@GetMapping("/listDict")
public ResponseResult<List<Map<String, Object>>> listDict(@ParameterObject FlowCategoryDto filter) {
List<FlowCategory> resultList =
flowCategoryService.getFlowCategoryList(MyModelUtil.copyTo(filter, FlowCategory.class), null);
return ResponseResult.success(
MyCommonUtil.toDictDataList(resultList, FlowCategory::getCategoryId, FlowCategory::getName));
}
/**
* 根据字典Id集合获取查询后的字典数据。
*
* @param dictIds 字典Id集合。
* @return 应答结果对象,包含字典形式的数据集合。
*/
@GetMapping("/listDictByIds")
public ResponseResult<List<Map<String, Object>>> listDictByIds(@RequestParam List<Long> dictIds) {
List<FlowCategory> resultList = flowCategoryService.getInList(new HashSet<>(dictIds));
return ResponseResult.success(
MyCommonUtil.toDictDataList(resultList, FlowCategory::getCategoryId, FlowCategory::getName));
}
private ResponseResult<FlowCategory> doVerifyAndGet(Long categoryId) {
String errorMessage;
if (MyCommonUtil.existBlankArgument(categoryId)) {
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
}
FlowCategory flowCategory = flowCategoryService.getById(categoryId);
if (flowCategory == null) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
}
TokenData tokenData = TokenData.takeFromRequest();
if (!StrUtil.equals(flowCategory.getAppCode(), tokenData.getAppCode())) {
errorMessage = "数据验证失败,当前应用并不存在该流程分类的定义!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
if (ObjectUtil.notEqual(flowCategory.getTenantId(), tokenData.getTenantId())) {
errorMessage = "数据验证失败,当前租户并不存在该流程分类的定义!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
return ResponseResult.success(flowCategory);
}
}

View File

@@ -0,0 +1,475 @@
package com.orangeforms.common.flow.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.tags.Tag;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.page.PageMethod;
import com.orangeforms.common.core.annotation.MyRequestBody;
import com.orangeforms.common.core.constant.ErrorCodeEnum;
import com.orangeforms.common.core.object.*;
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.log.annotation.OperationLog;
import com.orangeforms.common.log.model.constant.SysOperationLogType;
import com.orangeforms.common.flow.constant.FlowTaskType;
import com.orangeforms.common.flow.dto.*;
import com.orangeforms.common.flow.model.*;
import com.orangeforms.common.flow.model.constant.FlowEntryStatus;
import com.orangeforms.common.flow.service.*;
import com.orangeforms.common.flow.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.groups.Default;
import javax.xml.stream.XMLStreamException;
import java.util.*;
/**
* 工作流流程定义接口。
*
* @author Jerry
* @date 2024-07-02
*/
@Tag(name = "工作流流程定义接口")
@Slf4j
@RestController
@RequestMapping("${common-flow.urlPrefix}/flowEntry")
@ConditionalOnProperty(name = "common-flow.operationEnabled", havingValue = "true")
public class FlowEntryController {
@Autowired
private FlowEntryService flowEntryService;
@Autowired
private FlowCategoryService flowCategoryService;
@Autowired
private FlowEntryVariableService flowEntryVariableService;
@Autowired
private FlowApiService flowApiService;
@Autowired
private FlowTaskExtService flowTaskExtService;
/**
* 新增工作流对象数据。
*
* @param flowEntryDto 新增对象。
* @return 应答结果对象包含新增对象主键Id。
*/
@ApiOperationSupport(ignoreParameters = {"flowEntryDto.entryId"})
@SaCheckPermission("flowEntry.all")
@OperationLog(type = SysOperationLogType.ADD)
@PostMapping("/add")
public ResponseResult<Long> add(@MyRequestBody FlowEntryDto flowEntryDto) {
String errorMessage = MyCommonUtil.getModelValidationError(flowEntryDto);
if (errorMessage != null) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
FlowEntry flowEntry = MyModelUtil.copyTo(flowEntryDto, FlowEntry.class);
if (flowEntryService.existByProcessDefinitionKey(flowEntry.getProcessDefinitionKey())) {
errorMessage = "数据验证失败,该流程定义标识已存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
// 验证关联Id的数据合法性
CallResult callResult = flowEntryService.verifyRelatedData(flowEntry, null);
if (!callResult.isSuccess()) {
errorMessage = callResult.getErrorMessage();
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
flowEntry = flowEntryService.saveNew(flowEntry);
return ResponseResult.success(flowEntry.getEntryId());
}
/**
* 更新工作流对象数据。
*
* @param flowEntryDto 更新对象。
* @return 应答结果对象。
*/
@SaCheckPermission("flowEntry.all")
@OperationLog(type = SysOperationLogType.UPDATE)
@PostMapping("/update")
public ResponseResult<Void> update(@MyRequestBody FlowEntryDto flowEntryDto) {
String errorMessage = MyCommonUtil.getModelValidationError(flowEntryDto, Default.class, UpdateGroup.class);
if (errorMessage != null) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
FlowEntry flowEntry = MyModelUtil.copyTo(flowEntryDto, FlowEntry.class);
ResponseResult<FlowEntry> verifyResult = this.doVerifyAndGet(flowEntry.getEntryId());
if (!verifyResult.isSuccess()) {
return ResponseResult.errorFrom(verifyResult);
}
FlowEntry originalFlowEntry = verifyResult.getData();
if (ObjectUtil.notEqual(flowEntry.getProcessDefinitionKey(), originalFlowEntry.getProcessDefinitionKey())) {
if (originalFlowEntry.getStatus().equals(FlowEntryStatus.PUBLISHED)) {
errorMessage = "数据验证失败,当前流程为发布状态,流程标识不能修改!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
if (flowEntryService.existByProcessDefinitionKey(flowEntry.getProcessDefinitionKey())) {
errorMessage = "数据验证失败,该流程定义标识已存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
}
// 验证关联Id的数据合法性
CallResult callResult = flowEntryService.verifyRelatedData(flowEntry, originalFlowEntry);
if (!callResult.isSuccess()) {
errorMessage = callResult.getErrorMessage();
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
if (!flowEntryService.update(flowEntry, originalFlowEntry)) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
}
return ResponseResult.success();
}
/**
* 删除工作流对象数据。
*
* @param entryId 删除对象主键Id。
* @return 应答结果对象。
*/
@SaCheckPermission("flowEntry.all")
@OperationLog(type = SysOperationLogType.DELETE)
@PostMapping("/delete")
public ResponseResult<Void> delete(@MyRequestBody Long entryId) {
String errorMessage;
if (MyCommonUtil.existBlankArgument(entryId)) {
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
}
ResponseResult<FlowEntry> verifyResult = this.doVerifyAndGet(entryId);
if (!verifyResult.isSuccess()) {
return ResponseResult.errorFrom(verifyResult);
}
FlowEntry originalFlowEntry = verifyResult.getData();
if (originalFlowEntry.getStatus().equals(FlowEntryStatus.PUBLISHED)) {
errorMessage = "数据验证失败,当前流程为发布状态,不能删除!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
if (!flowEntryService.remove(entryId)) {
errorMessage = "数据操作失败,删除的对象不存在,请刷新后重试!";
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
}
return ResponseResult.success();
}
/**
* 发布工作流。
*
* @param entryId 流程主键Id。
* @return 应答结果对象。
*/
@SaCheckPermission("flowEntry.all")
@OperationLog(type = SysOperationLogType.PUBLISH)
@PostMapping("/publish")
public ResponseResult<Void> publish(@MyRequestBody(required = true) Long entryId) throws XMLStreamException {
String errorMessage;
ResponseResult<FlowEntry> verifyResult = this.doVerifyAndGet(entryId);
if (!verifyResult.isSuccess()) {
return ResponseResult.errorFrom(verifyResult);
}
FlowEntry flowEntry = verifyResult.getData();
if (StrUtil.isBlank(flowEntry.getBpmnXml())) {
errorMessage = "数据验证失败,该流程没有流程图不能被发布!";
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
}
ResponseResult<TaskInfoVo> taskInfoResult = this.verifyAndGetInitialTaskInfo(flowEntry);
if (!taskInfoResult.isSuccess()) {
return ResponseResult.errorFrom(taskInfoResult);
}
String taskInfo = taskInfoResult.getData() == null ? null : JSON.toJSONString(taskInfoResult.getData());
flowEntryService.publish(flowEntry, taskInfo);
return ResponseResult.success();
}
/**
* 列出符合过滤条件的工作流列表。
*
* @param flowEntryDtoFilter 过滤对象。
* @param orderParam 排序参数。
* @param pageParam 分页参数。
* @return 应答结果对象,包含查询结果集。
*/
@SaCheckPermission("flowEntry.all")
@PostMapping("/list")
public ResponseResult<MyPageData<FlowEntryVo>> list(
@MyRequestBody FlowEntryDto flowEntryDtoFilter,
@MyRequestBody MyOrderParam orderParam,
@MyRequestBody MyPageParam pageParam) {
if (pageParam != null) {
PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize());
}
FlowEntry flowEntryFilter = MyModelUtil.copyTo(flowEntryDtoFilter, FlowEntry.class);
String orderBy = MyOrderParam.buildOrderBy(orderParam, FlowEntry.class);
List<FlowEntry> flowEntryList = flowEntryService.getFlowEntryListWithRelation(flowEntryFilter, orderBy);
return ResponseResult.success(MyPageUtil.makeResponseData(flowEntryList, FlowEntryVo.class));
}
/**
* 查看指定工作流对象详情。
*
* @param entryId 指定对象主键Id。
* @return 应答结果对象,包含对象详情。
*/
@SaCheckPermission("flowEntry.all")
@GetMapping("/view")
public ResponseResult<FlowEntryVo> view(@RequestParam Long entryId) {
ResponseResult<FlowEntry> verifyResult = this.doVerifyAndGet(entryId);
if (!verifyResult.isSuccess()) {
return ResponseResult.errorFrom(verifyResult);
}
FlowEntry flowEntry = flowEntryService.getByIdWithRelation(entryId, MyRelationParam.full());
if (flowEntry == null) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
}
return ResponseResult.success(flowEntry, FlowEntryVo.class);
}
/**
* 列出指定流程的发布版本列表。
*
* @param entryId 流程主键Id。
* @return 应答结果对象,包含流程发布列表数据。
*/
@SaCheckPermission("flowEntry.all")
@GetMapping("/listFlowEntryPublish")
public ResponseResult<List<FlowEntryPublishVo>> listFlowEntryPublish(@RequestParam Long entryId) {
ResponseResult<FlowEntry> verifyResult = this.doVerifyAndGet(entryId);
if (!verifyResult.isSuccess()) {
return ResponseResult.errorFrom(verifyResult);
}
List<FlowEntryPublish> flowEntryPublishList = flowEntryService.getFlowEntryPublishList(entryId);
return ResponseResult.success(MyModelUtil.copyCollectionTo(flowEntryPublishList, FlowEntryPublishVo.class));
}
/**
* 以字典形式返回全部FlowEntry数据集合。字典的键值为[entryId, procDefinitionName]。
* 白名单接口,登录用户均可访问。
*
* @param filter 过滤对象。
* @return 应答结果对象,包含的数据为 List<Map<String, String>>map中包含两条记录key的值分别是id和namevalue对应具体数据。
*/
@GetMapping("/listDict")
public ResponseResult<List<Map<String, Object>>> listDict(@ParameterObject FlowEntryDto filter) {
List<FlowEntry> resultList =
flowEntryService.getFlowEntryList(MyModelUtil.copyTo(filter, FlowEntry.class), null);
return ResponseResult.success(
MyCommonUtil.toDictDataList(resultList, FlowEntry::getEntryId, FlowEntry::getProcessDefinitionName));
}
/**
* 获取所有流程分类和流程定义的列表。白名单接口。
*
* @return 所有流程分类和流程定义的列表
*/
@GetMapping("/listAll")
public ResponseResult<JSONObject> listAll() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("flowEntryList", flowEntryService.getFlowEntryList(null, null));
jsonObject.put("flowCategoryList", flowCategoryService.getFlowCategoryList(null, null));
return ResponseResult.success(jsonObject);
}
/**
* 白名单接口根据流程Id获取流程引擎需要的流程标识和流程名称。
*
* @param entryId 流程Id。
* @return 流程的部分数据。
*/
@GetMapping("/viewDict")
public ResponseResult<Map<String, Object>> viewDict(@RequestParam Long entryId) {
ResponseResult<FlowEntry> verifyResult = this.doVerifyAndGet(entryId);
if (!verifyResult.isSuccess()) {
return ResponseResult.errorFrom(verifyResult);
}
FlowEntry flowEntry = verifyResult.getData();
Map<String, Object> resultMap = new HashMap<>(2);
resultMap.put("processDefinitionKey", flowEntry.getProcessDefinitionKey());
resultMap.put("processDefinitionName", flowEntry.getProcessDefinitionName());
return ResponseResult.success(resultMap);
}
/**
* 切换指定工作的发布主版本。
*
* @param entryId 工作流主键Id。
* @param newEntryPublishId 新的工作流发布主版本对象的主键Id。
* @return 应答结果对象。
*/
@SaCheckPermission("flowEntry.all")
@OperationLog(type = SysOperationLogType.UPDATE)
@PostMapping("/updateMainVersion")
public ResponseResult<Void> updateMainVersion(
@MyRequestBody(required = true) Long entryId,
@MyRequestBody(required = true) Long newEntryPublishId) {
String errorMessage;
ResponseResult<FlowEntry> verifyResult = this.doVerifyAndGet(entryId);
if (!verifyResult.isSuccess()) {
return ResponseResult.errorFrom(verifyResult);
}
FlowEntryPublish flowEntryPublish = flowEntryService.getFlowEntryPublishFromCache(newEntryPublishId);
if (flowEntryPublish == null) {
errorMessage = "数据验证失败,当前流程发布版本并不存在,请刷新后重试!";
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
}
if (ObjectUtil.notEqual(entryId, flowEntryPublish.getEntryId())) {
errorMessage = "数据验证失败,当前工作流并不包含该工作流发布版本数据,请刷新后重试!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
if (BooleanUtil.isTrue(flowEntryPublish.getMainVersion())) {
errorMessage = "数据验证失败,该版本已经为当前工作流的发布主版本,不能重复设置!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
flowEntryService.updateFlowEntryMainVersion(flowEntryService.getById(entryId), flowEntryPublish);
return ResponseResult.success();
}
/**
* 挂起工作流的指定发布版本。
*
* @param entryPublishId 工作发布Id。
* @return 应答结果对象。
*/
@SaCheckPermission("flowEntry.all")
@OperationLog(type = SysOperationLogType.SUSPEND)
@PostMapping("/suspendFlowEntryPublish")
public ResponseResult<Void> suspendFlowEntryPublish(@MyRequestBody(required = true) Long entryPublishId) {
String errorMessage;
FlowEntryPublish flowEntryPublish = flowEntryService.getFlowEntryPublishFromCache(entryPublishId);
if (flowEntryPublish == null) {
errorMessage = "数据验证失败,当前流程发布版本并不存在,请刷新后重试!";
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
}
ResponseResult<FlowEntry> verifyResult = this.doVerifyAndGet(flowEntryPublish.getEntryId());
if (!verifyResult.isSuccess()) {
return ResponseResult.errorFrom(verifyResult);
}
if (BooleanUtil.isFalse(flowEntryPublish.getActiveStatus())) {
errorMessage = "数据验证失败,当前流程发布版本已处于挂起状态!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
flowEntryService.suspendFlowEntryPublish(flowEntryPublish);
return ResponseResult.success();
}
/**
* 激活工作流的指定发布版本。
*
* @param entryPublishId 工作发布Id。
* @return 应答结果对象。
*/
@SaCheckPermission("flowEntry.all")
@OperationLog(type = SysOperationLogType.RESUME)
@PostMapping("/activateFlowEntryPublish")
public ResponseResult<Void> activateFlowEntryPublish(@MyRequestBody(required = true) Long entryPublishId) {
String errorMessage;
FlowEntryPublish flowEntryPublish = flowEntryService.getFlowEntryPublishFromCache(entryPublishId);
if (flowEntryPublish == null) {
errorMessage = "数据验证失败,当前流程发布版本并不存在,请刷新后重试!";
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
}
ResponseResult<FlowEntry> verifyResult = this.doVerifyAndGet(flowEntryPublish.getEntryId());
if (!verifyResult.isSuccess()) {
return ResponseResult.errorFrom(verifyResult);
}
if (BooleanUtil.isTrue(flowEntryPublish.getActiveStatus())) {
errorMessage = "数据验证失败,当前流程发布版本已处于激活状态!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
flowEntryService.activateFlowEntryPublish(flowEntryPublish);
return ResponseResult.success();
}
private ResponseResult<FlowEntry> doVerifyAndGet(Long entryId) {
String errorMessage;
if (MyCommonUtil.existBlankArgument(entryId)) {
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
}
FlowEntry flowEntry = flowEntryService.getById(entryId);
if (flowEntry == null) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
}
TokenData tokenData = TokenData.takeFromRequest();
if (!StrUtil.equals(flowEntry.getAppCode(), tokenData.getAppCode())) {
errorMessage = "数据验证失败,当前应用并不存在该流程定义!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
if (ObjectUtil.notEqual(flowEntry.getTenantId(), tokenData.getTenantId())) {
errorMessage = "数据验证失败,当前租户并不存在该流程定义!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
return ResponseResult.success(flowEntry);
}
private ResponseResult<TaskInfoVo> verifyAndGetInitialTaskInfo(FlowEntry flowEntry) throws XMLStreamException {
String errorMessage;
BpmnModel bpmnModel = flowApiService.convertToBpmnModel(flowEntry.getBpmnXml());
Process process = bpmnModel.getMainProcess();
if (process == null) {
errorMessage = "数据验证失败,当前流程标识 [" + flowEntry.getProcessDefinitionKey() + "] 关联的流程模型并不存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
Collection<FlowElement> elementList = process.getFlowElements();
FlowElement startEvent = null;
// 这里我们只定位流程模型中的第二个节点。
for (FlowElement flowElement : elementList) {
if (flowElement instanceof StartEvent) {
startEvent = flowElement;
break;
}
}
if (startEvent == null) {
errorMessage = "数据验证失败,当前流程图没有包含 [开始事件] 节点,请修改流程图!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
FlowElement firstTask = this.findFirstTask(elementList, startEvent);
if (firstTask == null) {
errorMessage = "数据验证失败,当前流程图没有包含 [开始事件] 节点没有任何连线,请修改流程图!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
TaskInfoVo taskInfoVo;
if (firstTask instanceof UserTask) {
UserTask userTask = (UserTask) firstTask;
String formKey = userTask.getFormKey();
if (StrUtil.isNotBlank(formKey)) {
taskInfoVo = JSON.parseObject(formKey, TaskInfoVo.class);
} else {
taskInfoVo = new TaskInfoVo();
}
taskInfoVo.setAssignee(userTask.getAssignee());
taskInfoVo.setTaskKey(userTask.getId());
taskInfoVo.setTaskType(FlowTaskType.USER_TYPE);
Map<String, List<ExtensionElement>> extensionMap = userTask.getExtensionElements();
if (MapUtil.isNotEmpty(extensionMap)) {
taskInfoVo.setOperationList(flowTaskExtService.buildOperationListExtensionElement(extensionMap));
taskInfoVo.setVariableList(flowTaskExtService.buildVariableListExtensionElement(extensionMap));
}
} else {
taskInfoVo = new TaskInfoVo();
taskInfoVo.setTaskType(FlowTaskType.OTHER_TYPE);
}
return ResponseResult.success(taskInfoVo);
}
private FlowElement findFirstTask(Collection<FlowElement> elementList, FlowElement startEvent) {
for (FlowElement flowElement : elementList) {
if (flowElement instanceof SequenceFlow) {
SequenceFlow sequenceFlow = (SequenceFlow) flowElement;
if (sequenceFlow.getSourceFlowElement().equals(startEvent)) {
return sequenceFlow.getTargetFlowElement();
}
}
}
return null;
}
}

View File

@@ -0,0 +1,159 @@
package com.orangeforms.common.flow.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.tags.Tag;
import com.github.pagehelper.page.PageMethod;
import com.orangeforms.common.flow.vo.*;
import com.orangeforms.common.flow.dto.*;
import com.orangeforms.common.flow.model.*;
import com.orangeforms.common.flow.service.*;
import com.orangeforms.common.core.object.*;
import com.orangeforms.common.core.util.*;
import com.orangeforms.common.core.constant.*;
import com.orangeforms.common.core.annotation.MyRequestBody;
import com.orangeforms.common.core.validator.UpdateGroup;
import com.orangeforms.common.log.annotation.OperationLog;
import com.orangeforms.common.log.model.constant.SysOperationLogType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import jakarta.validation.groups.Default;
/**
* 工作流流程变量接口。
*
* @author Jerry
* @date 2024-07-02
*/
@Tag(name = "工作流流程变量接口")
@Slf4j
@RestController
@RequestMapping("${common-flow.urlPrefix}/flowEntryVariable")
@ConditionalOnProperty(name = "common-flow.operationEnabled", havingValue = "true")
public class FlowEntryVariableController {
@Autowired
private FlowEntryVariableService flowEntryVariableService;
/**
* 新增流程变量数据。
*
* @param flowEntryVariableDto 新增对象。
* @return 应答结果对象包含新增对象主键Id。
*/
@ApiOperationSupport(ignoreParameters = {"flowEntryVariableDto.variableId"})
@SaCheckPermission("flowEntry.all")
@OperationLog(type = SysOperationLogType.ADD)
@PostMapping("/add")
public ResponseResult<Long> add(@MyRequestBody FlowEntryVariableDto flowEntryVariableDto) {
String errorMessage = MyCommonUtil.getModelValidationError(flowEntryVariableDto);
if (errorMessage != null) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
FlowEntryVariable flowEntryVariable = MyModelUtil.copyTo(flowEntryVariableDto, FlowEntryVariable.class);
flowEntryVariable = flowEntryVariableService.saveNew(flowEntryVariable);
return ResponseResult.success(flowEntryVariable.getVariableId());
}
/**
* 更新流程变量数据。
*
* @param flowEntryVariableDto 更新对象。
* @return 应答结果对象。
*/
@SaCheckPermission("flowEntry.all")
@OperationLog(type = SysOperationLogType.UPDATE)
@PostMapping("/update")
public ResponseResult<Void> update(@MyRequestBody FlowEntryVariableDto flowEntryVariableDto) {
String errorMessage = MyCommonUtil.getModelValidationError(flowEntryVariableDto, Default.class, UpdateGroup.class);
if (errorMessage != null) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
FlowEntryVariable flowEntryVariable = MyModelUtil.copyTo(flowEntryVariableDto, FlowEntryVariable.class);
FlowEntryVariable originalFlowEntryVariable = flowEntryVariableService.getById(flowEntryVariable.getVariableId());
if (originalFlowEntryVariable == null) {
// NOTE: 修改下面方括号中的话述
errorMessage = "数据验证失败,当前 [数据] 并不存在,请刷新后重试!";
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
}
if (!flowEntryVariableService.update(flowEntryVariable, originalFlowEntryVariable)) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
}
return ResponseResult.success();
}
/**
* 删除流程变量数据。
*
* @param variableId 删除对象主键Id。
* @return 应答结果对象。
*/
@SaCheckPermission("flowEntry.all")
@OperationLog(type = SysOperationLogType.DELETE)
@PostMapping("/delete")
public ResponseResult<Void> delete(@MyRequestBody Long variableId) {
String errorMessage;
if (MyCommonUtil.existBlankArgument(variableId)) {
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
}
// 验证关联Id的数据合法性
FlowEntryVariable originalFlowEntryVariable = flowEntryVariableService.getById(variableId);
if (originalFlowEntryVariable == null) {
// NOTE: 修改下面方括号中的话述
errorMessage = "数据验证失败,当前 [对象] 并不存在,请刷新后重试!";
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
}
if (!flowEntryVariableService.remove(variableId)) {
errorMessage = "数据操作失败,删除的对象不存在,请刷新后重试!";
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
}
return ResponseResult.success();
}
/**
* 列出符合过滤条件的流程变量列表。
*
* @param flowEntryVariableDtoFilter 过滤对象。
* @param orderParam 排序参数。
* @param pageParam 分页参数。
* @return 应答结果对象,包含查询结果集。
*/
@SaCheckPermission("flowEntry.all")
@PostMapping("/list")
public ResponseResult<MyPageData<FlowEntryVariableVo>> list(
@MyRequestBody FlowEntryVariableDto flowEntryVariableDtoFilter,
@MyRequestBody MyOrderParam orderParam,
@MyRequestBody MyPageParam pageParam) {
if (pageParam != null) {
PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize());
}
FlowEntryVariable flowEntryVariableFilter = MyModelUtil.copyTo(flowEntryVariableDtoFilter, FlowEntryVariable.class);
String orderBy = MyOrderParam.buildOrderBy(orderParam, FlowEntryVariable.class);
List<FlowEntryVariable> flowEntryVariableList =
flowEntryVariableService.getFlowEntryVariableListWithRelation(flowEntryVariableFilter, orderBy);
return ResponseResult.success(MyPageUtil.makeResponseData(flowEntryVariableList, FlowEntryVariableVo.class));
}
/**
* 查看指定流程变量对象详情。
*
* @param variableId 指定对象主键Id。
* @return 应答结果对象,包含对象详情。
*/
@SaCheckPermission("flowEntry.all")
@GetMapping("/view")
public ResponseResult<FlowEntryVariableVo> view(@RequestParam Long variableId) {
if (MyCommonUtil.existBlankArgument(variableId)) {
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
}
FlowEntryVariable flowEntryVariable = flowEntryVariableService.getByIdWithRelation(variableId, MyRelationParam.full());
if (flowEntryVariable == null) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
}
return ResponseResult.success(flowEntryVariable, FlowEntryVariableVo.class);
}
}

View File

@@ -0,0 +1,110 @@
package com.orangeforms.common.flow.controller;
import io.swagger.v3.oas.annotations.tags.Tag;
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;
import com.github.pagehelper.page.PageMethod;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 工作流消息接口。
*
* @author Jerry
* @date 2024-07-02
*/
@Tag(name = "工作流消息接口")
@Slf4j
@RestController
@RequestMapping("${common-flow.urlPrefix}/flowMessage")
@ConditionalOnProperty(name = "common-flow.operationEnabled", havingValue = "true")
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 应答结果对象,包含查询结果集。
*/
@PostMapping("/listRemindingTask")
public ResponseResult<MyPageData<FlowMessageVo>> listRemindingTask(@MyRequestBody MyPageParam pageParam) {
if (pageParam != null) {
PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize(), pageParam.getCount());
}
List<FlowMessage> flowMessageList = flowMessageService.getRemindingMessageListByUser();
return ResponseResult.success(MyPageUtil.makeResponseData(flowMessageList, FlowMessageVo.class));
}
/**
* 获取当前用户的抄送消息列表。
* 不仅仅包含,其中包括当前用户所属角色、部门和岗位的候选组抄送消息。
* 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(), pageParam.getCount());
}
List<FlowMessage> flowMessageList = flowMessageService.getCopyMessageListByUser(read);
return ResponseResult.success(MyPageUtil.makeResponseData(flowMessageList, FlowMessageVo.class));
}
/**
* 读取抄送消息,同时更新当前用户对指定抄送消息的读取状态。
*
* @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();
}
}

View File

@@ -0,0 +1,941 @@
package com.orangeforms.common.flow.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.orangeforms.common.core.annotation.DisableDataFilter;
import com.orangeforms.common.core.annotation.MyRequestBody;
import com.orangeforms.common.core.constant.ErrorCodeEnum;
import com.orangeforms.common.core.object.*;
import com.orangeforms.common.core.util.MyModelUtil;
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.exception.FlowOperationException;
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;
import com.orangeforms.common.flow.vo.FlowUserInfoVo;
import com.orangeforms.common.flow.vo.TaskInfoVo;
import com.orangeforms.common.log.annotation.OperationLog;
import com.orangeforms.common.log.model.constant.SysOperationLogType;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;
/**
* 工作流流程操作接口。
*
* @author Jerry
* @date 2024-07-02
*/
@Tag(name = "工作流流程操作接口")
@Slf4j
@RestController
@RequestMapping("${common-flow.urlPrefix}/flowOperation")
@ConditionalOnProperty(name = "common-flow.operationEnabled", havingValue = "true")
public class FlowOperationController {
@Autowired
private FlowEntryService flowEntryService;
@Autowired
private FlowTaskCommentService flowTaskCommentService;
@Autowired
private FlowTaskExtService flowTaskExtService;
@Autowired
private FlowApiService flowApiService;
@Autowired
private FlowWorkOrderService flowWorkOrderService;
@Autowired
private FlowMessageService flowMessageService;
@Autowired
private FlowOperationHelper flowOperationHelper;
@Autowired
private FlowCustomExtFactory flowCustomExtFactory;
@Autowired
private FlowMultiInstanceTransService flowMultiInstanceTransService;
private static final String ACTIVE_MULTI_INST_TASK = "activeMultiInstanceTask";
private static final String SHOW_NAME = "showName";
private static final String INSTANCE_ID = "processInstanceId";
/**
* 获取开始节点之后的第一个任务节点的数据。
*
* @param processDefinitionKey 流程标识。
* @return 任务节点的自定义对象数据。
*/
@GetMapping("/viewInitialTaskInfo")
public ResponseResult<TaskInfoVo> viewInitialTaskInfo(@RequestParam String processDefinitionKey) {
ResponseResult<FlowEntry> flowEntryResult = flowOperationHelper.verifyAndGetFlowEntry(processDefinitionKey);
if (!flowEntryResult.isSuccess()) {
return ResponseResult.errorFrom(flowEntryResult);
}
FlowEntryPublish flowEntryPublish = flowEntryResult.getData().getMainFlowEntryPublish();
String initTaskInfo = flowEntryPublish.getInitTaskInfo();
TaskInfoVo taskInfo = StrUtil.isBlank(initTaskInfo)
? null : JSON.parseObject(initTaskInfo, TaskInfoVo.class);
if (taskInfo != null) {
String loginName = TokenData.takeFromRequest().getLoginName();
taskInfo.setAssignedMe(StrUtil.equalsAny(
taskInfo.getAssignee(), loginName, FlowConstant.START_USER_NAME_VAR));
}
return ResponseResult.success(taskInfo);
}
/**
* 获取流程运行时指定任务的信息。
*
* @param processDefinitionId 流程引擎的定义Id。
* @param processInstanceId 流程引擎的实例Id。
* @param taskId 流程引擎的任务Id。
* @return 任务节点的自定义对象数据。
*/
@GetMapping("/viewRuntimeTaskInfo")
public ResponseResult<TaskInfoVo> viewRuntimeTaskInfo(
@RequestParam String processDefinitionId,
@RequestParam String processInstanceId,
@RequestParam String taskId) {
Task task = flowApiService.getProcessInstanceActiveTask(processInstanceId, taskId);
ResponseResult<TaskInfoVo> taskInfoResult = flowOperationHelper.verifyAndGetRuntimeTaskInfo(task);
if (!taskInfoResult.isSuccess()) {
return ResponseResult.errorFrom(taskInfoResult);
}
TaskInfoVo taskInfoVo = taskInfoResult.getData();
FlowTaskExt flowTaskExt =
flowTaskExtService.getByProcessDefinitionIdAndTaskId(processDefinitionId, taskInfoVo.getTaskKey());
if (flowTaskExt != null) {
if (StrUtil.isNotBlank(flowTaskExt.getOperationListJson())) {
taskInfoVo.setOperationList(JSON.parseArray(flowTaskExt.getOperationListJson(), JSONObject.class));
}
if (StrUtil.isNotBlank(flowTaskExt.getVariableListJson())) {
taskInfoVo.setVariableList(JSON.parseArray(flowTaskExt.getVariableListJson(), JSONObject.class));
}
}
return ResponseResult.success(taskInfoVo);
}
/**
* 获取流程运行时指定任务的信息。
*
* @param processDefinitionId 流程引擎的定义Id。
* @param processInstanceId 流程引擎的实例Id。
* @param taskId 流程引擎的任务Id。
* @return 任务节点的自定义对象数据。
*/
@GetMapping("/viewHistoricTaskInfo")
public ResponseResult<TaskInfoVo> viewHistoricTaskInfo(
@RequestParam String processDefinitionId,
@RequestParam String processInstanceId,
@RequestParam String taskId) {
String errorMessage;
HistoricTaskInstance taskInstance = flowApiService.getHistoricTaskInstance(processInstanceId, taskId);
String loginName = TokenData.takeFromRequest().getLoginName();
if (!StrUtil.equals(taskInstance.getAssignee(), loginName)) {
errorMessage = "数据验证失败,当前用户不是指派人!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
TaskInfoVo taskInfoVo = JSON.parseObject(taskInstance.getFormKey(), TaskInfoVo.class);
FlowTaskExt flowTaskExt =
flowTaskExtService.getByProcessDefinitionIdAndTaskId(processDefinitionId, taskInstance.getTaskDefinitionKey());
if (flowTaskExt != null) {
if (StrUtil.isNotBlank(flowTaskExt.getOperationListJson())) {
taskInfoVo.setOperationList(JSON.parseArray(flowTaskExt.getOperationListJson(), JSONObject.class));
}
if (StrUtil.isNotBlank(flowTaskExt.getVariableListJson())) {
taskInfoVo.setVariableList(JSON.parseArray(flowTaskExt.getVariableListJson(), JSONObject.class));
}
}
return ResponseResult.success(taskInfoVo);
}
/**
* 获取第一个提交表单数据的任务信息。
*
* @param processInstanceId 流程实例Id。
* @return 任务节点的自定义对象数据。
*/
@GetMapping("/viewInitialHistoricTaskInfo")
public ResponseResult<TaskInfoVo> viewInitialHistoricTaskInfo(@RequestParam String processInstanceId) {
String errorMessage;
List<FlowTaskComment> taskCommentList =
flowTaskCommentService.getFlowTaskCommentList(processInstanceId);
if (CollUtil.isEmpty(taskCommentList)) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
}
FlowTaskComment taskComment = taskCommentList.get(0);
HistoricTaskInstance task = flowApiService.getHistoricTaskInstance(processInstanceId, taskComment.getTaskId());
if (StrUtil.isBlank(task.getFormKey())) {
errorMessage = "数据验证失败指定任务的formKey属性不存在请重新修改流程图";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
TaskInfoVo taskInfo = JSON.parseObject(task.getFormKey(), TaskInfoVo.class);
taskInfo.setTaskKey(task.getTaskDefinitionKey());
return ResponseResult.success(taskInfo);
}
/**
* 获取任务的用户信息列表。
*
* @param processDefinitionId 流程定义Id。
* @param processInstanceId 流程实例Id。
* @param taskId 流程任务Id。
* @param historic 是否为历史任务。
* @return 任务相关的用户信息列表。
*/
@DisableDataFilter
@GetMapping("/viewTaskUserInfo")
public ResponseResult<List<FlowUserInfoVo>> viewTaskUserInfo(
@RequestParam String processDefinitionId,
@RequestParam String processInstanceId,
@RequestParam String taskId,
@RequestParam Boolean historic) {
TaskInfo taskInfo;
HistoricTaskInstance hisotricTask;
if (BooleanUtil.isFalse(historic)) {
taskInfo = flowApiService.getTaskById(taskId);
if (taskInfo == null) {
hisotricTask = flowApiService.getHistoricTaskInstance(processInstanceId, taskId);
taskInfo = hisotricTask;
historic = true;
}
} else {
hisotricTask = flowApiService.getHistoricTaskInstance(processInstanceId, taskId);
taskInfo = hisotricTask;
}
if (taskInfo == null) {
String errorMessage = "数据验证失败任务Id不存在";
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
}
String taskKey = taskInfo.getTaskDefinitionKey();
FlowTaskExt taskExt = flowTaskExtService.getByProcessDefinitionIdAndTaskId(processDefinitionId, taskKey);
boolean isMultiInstanceTask = flowApiService.isMultiInstanceTask(taskInfo.getProcessDefinitionId(), taskKey);
List<FlowUserInfoVo> resultUserInfoList =
flowTaskExtService.getCandidateUserInfoList(processInstanceId, taskExt, taskInfo, isMultiInstanceTask, historic);
if (BooleanUtil.isTrue(historic) || isMultiInstanceTask) {
List<FlowTaskComment> taskCommentList = buildApprovedFlowTaskCommentList(taskInfo, isMultiInstanceTask);
Map<String, FlowUserInfoVo> resultUserInfoMap =
resultUserInfoList.stream().collect(Collectors.toMap(FlowUserInfoVo::getLoginName, c -> c));
for (FlowTaskComment taskComment : taskCommentList) {
FlowUserInfoVo flowUserInfoVo = resultUserInfoMap.get(taskComment.getCreateLoginName());
if (flowUserInfoVo != null) {
flowUserInfoVo.setLastApprovalTime(taskComment.getCreateTime());
}
}
}
return ResponseResult.success(resultUserInfoList);
}
/**
* 获取多实例会签任务的指派人列表。
* NOTE: 白名单接口。
*
* @param processInstanceId 流程实例Id。
* @param taskId 多实例任务的上一级任务Id。
* @return 应答结果,指定会签任务的指派人列表。
*/
@GetMapping("/listMultiSignAssignees")
public ResponseResult<List<JSONObject>> listMultiSignAssignees(
@RequestParam String processInstanceId, @RequestParam String taskId) {
ResponseResult<JSONObject> verifyResult = this.doVerifyMultiSign(processInstanceId, taskId);
if (!verifyResult.isSuccess()) {
return ResponseResult.errorFrom(verifyResult);
}
Task activeMultiInstanceTask =
verifyResult.getData().getObject(ACTIVE_MULTI_INST_TASK, Task.class);
String multiInstanceExecId = flowApiService.getExecutionVariableStringWithSafe(
activeMultiInstanceTask.getExecutionId(), FlowConstant.MULTI_SIGN_TASK_EXECUTION_ID_VAR);
FlowMultiInstanceTrans trans =
flowMultiInstanceTransService.getWithAssigneeListByMultiInstanceExecId(multiInstanceExecId);
List<FlowTaskComment> commentList =
flowTaskCommentService.getFlowTaskCommentListByMultiInstanceExecId(multiInstanceExecId);
List<String> assigneeList = StrUtil.split(trans.getAssigneeList(), ",");
Set<String> approvedAssigneeSet = commentList.stream()
.map(FlowTaskComment::getCreateLoginName).collect(Collectors.toSet());
List<JSONObject> resultList = new LinkedList<>();
Map<String, String> usernameMap =
flowCustomExtFactory.getFlowIdentityExtHelper().mapUserShowNameByLoginName(new HashSet<>(assigneeList));
for (String assignee : assigneeList) {
JSONObject resultData = new JSONObject();
resultData.put("assignee", assignee);
resultData.put(SHOW_NAME, usernameMap.get(assignee));
resultData.put("approved", approvedAssigneeSet.contains(assignee));
resultList.add(resultData);
}
return ResponseResult.success(resultList);
}
/**
* 提交多实例加签或减签。
* NOTE: 白名单接口。
*
* @param processInstanceId 流程实例Id。
* @param taskId 多实例任务的上一级任务Id。
* @param newAssignees 加签减签人列表,多个指派人之间逗号分隔。
* @param isAdd 是否为加签如果没有该参数为了保持兼容性缺省值为true。
* @return 应答结果。
*/
@PostMapping("/submitConsign")
public ResponseResult<Void> submitConsign(
@MyRequestBody(required = true) String processInstanceId,
@MyRequestBody(required = true) String taskId,
@MyRequestBody(required = true) String newAssignees,
@MyRequestBody Boolean isAdd) {
String errorMessage;
ResponseResult<JSONObject> verifyResult = this.doVerifyMultiSign(processInstanceId, taskId);
if (!verifyResult.isSuccess()) {
return ResponseResult.errorFrom(verifyResult);
}
HistoricTaskInstance taskInstance =
verifyResult.getData().getObject("taskInstance", HistoricTaskInstance.class);
Task activeMultiInstanceTask =
verifyResult.getData().getObject(ACTIVE_MULTI_INST_TASK, Task.class);
String multiInstanceExecId = flowApiService.getExecutionVariableStringWithSafe(
activeMultiInstanceTask.getExecutionId(), FlowConstant.MULTI_SIGN_TASK_EXECUTION_ID_VAR);
JSONArray assigneeArray = JSON.parseArray(newAssignees);
if (isAdd == null) {
isAdd = true;
}
if (!isAdd) {
List<FlowTaskComment> commentList =
flowTaskCommentService.getFlowTaskCommentListByMultiInstanceExecId(multiInstanceExecId);
if (CollUtil.isNotEmpty(commentList)) {
Set<String> approvedAssigneeSet = commentList.stream()
.map(FlowTaskComment::getCreateLoginName).collect(Collectors.toSet());
String loginName = this.findExistAssignee(approvedAssigneeSet, assigneeArray);
if (loginName != null) {
errorMessage = "数据验证失败,用户 [" + loginName + "] 已经审批,不能减签该用户!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
}
} else {
// 避免同一人被重复加签。
FlowMultiInstanceTrans trans =
flowMultiInstanceTransService.getWithAssigneeListByMultiInstanceExecId(multiInstanceExecId);
Set<String> assigneeSet = new HashSet<>(StrUtil.split(trans.getAssigneeList(), ","));
String loginName = this.findExistAssignee(assigneeSet, assigneeArray);
if (loginName != null) {
errorMessage = "数据验证失败,用户 [" + loginName + "] 已经是会签人,不能重复指定!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
}
try {
flowApiService.submitConsign(taskInstance, activeMultiInstanceTask, newAssignees, isAdd);
} catch (FlowOperationException e) {
errorMessage = e.getMessage();
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
return ResponseResult.success();
}
/**
* 返回当前用户待办的任务列表。
*
* @param processDefinitionKey 流程标识。
* @param processDefinitionName 流程定义名 (模糊查询)。
* @param taskName 任务名称 (模糊查询)。
* @param pageParam 分页对象。
* @return 返回当前用户待办的任务列表。如果指定流程标识,则仅返回该流程的待办任务列表。
*/
@DisableDataFilter
@PostMapping("/listRuntimeTask")
public ResponseResult<MyPageData<FlowTaskVo>> listRuntimeTask(
@MyRequestBody String processDefinitionKey,
@MyRequestBody String processDefinitionName,
@MyRequestBody String taskName,
@MyRequestBody(required = true) MyPageParam pageParam) {
String username = TokenData.takeFromRequest().getLoginName();
MyPageData<Task> pageData = flowApiService.getTaskListByUserName(
username, processDefinitionKey, processDefinitionName, taskName, pageParam);
List<FlowTaskVo> flowTaskVoList = flowApiService.convertToFlowTaskList(pageData.getDataList());
return ResponseResult.success(MyPageUtil.makeResponseData(flowTaskVoList, pageData.getTotalCount()));
}
/**
* 返回当前用户待办的任务数量。
*
* @return 返回当前用户待办的任务数量。
*/
@PostMapping("/countRuntimeTask")
public ResponseResult<Long> countRuntimeTask() {
String username = TokenData.takeFromRequest().getLoginName();
long totalCount = flowApiService.getTaskCountByUserName(username);
return ResponseResult.success(totalCount);
}
/**
* 主动驳回当前的待办任务到开始节点,只用当前待办任务的指派人或者候选者才能完成该操作。
*
* @param processInstanceId 流程实例Id。
* @param taskId 待办任务Id。
* @param taskComment 驳回备注。
* @return 操作应答结果。
*/
@PostMapping("/rejectToStartUserTask")
public ResponseResult<Void> rejectToStartUserTask(
@MyRequestBody(required = true) String processInstanceId,
@MyRequestBody(required = true) String taskId,
@MyRequestBody(required = true) String taskComment) {
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, taskComment);
if (!result.isSuccess()) {
return ResponseResult.errorFrom(result);
}
return ResponseResult.success();
}
/**
* 主动驳回当前的待办任务,只用当前待办任务的指派人或者候选者才能完成该操作。
*
* @param processInstanceId 流程实例Id。
* @param taskId 待办任务Id。
* @param taskComment 驳回备注。
* @return 操作应答结果。
*/
@PostMapping("/rejectRuntimeTask")
public ResponseResult<Void> rejectRuntimeTask(
@MyRequestBody(required = true) String processInstanceId,
@MyRequestBody(required = true) String taskId,
@MyRequestBody(required = true) String taskComment) {
String errorMessage;
ResponseResult<Task> taskResult =
flowOperationHelper.verifySubmitAndGetTask(processInstanceId, taskId, null);
if (!taskResult.isSuccess()) {
return ResponseResult.errorFrom(taskResult);
}
CallResult result = flowApiService.backToRuntimeTask(taskResult.getData(), null, true, taskComment);
if (!result.isSuccess()) {
return ResponseResult.errorFrom(result);
}
return ResponseResult.success();
}
/**
* 撤回当前用户提交的,但是尚未被审批的待办任务。只有已办任务的指派人才能完成该操作。
*
* @param processInstanceId 流程实例Id。
* @param taskId 待撤回的已办任务Id。
* @param taskComment 撤回备注。
* @return 操作应答结果。
*/
@PostMapping("/revokeHistoricTask")
public ResponseResult<Void> revokeHistoricTask(
@MyRequestBody(required = true) String processInstanceId,
@MyRequestBody(required = true) String taskId,
@MyRequestBody(required = true) String taskComment) {
String errorMessage;
if (!flowApiService.existActiveProcessInstance(processInstanceId)) {
errorMessage = "数据验证失败,当前流程实例已经结束,不能执行撤回!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
HistoricTaskInstance taskInstance = flowApiService.getHistoricTaskInstance(processInstanceId, taskId);
if (taskInstance == null) {
errorMessage = "数据验证失败,当前任务不存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
if (!StrUtil.equals(taskInstance.getAssignee(), TokenData.takeFromRequest().getLoginName())) {
errorMessage = "数据验证失败,任务指派人与当前用户不匹配!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
FlowTaskComment latestComment = flowTaskCommentService.getLatestFlowTaskComment(processInstanceId);
if (latestComment == null) {
errorMessage = "数据验证失败,当前实例没有任何审批提交记录!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
if (!latestComment.getTaskId().equals(taskId)) {
errorMessage = "数据验证失败,当前审批任务已被办理,不能撤回!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
List<Task> activeTaskList = flowApiService.getProcessInstanceActiveTaskList(processInstanceId);
if (CollUtil.isEmpty(activeTaskList)) {
errorMessage = "数据验证失败,当前流程没有任何待办任务!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
if (latestComment.getApprovalType().equals(FlowApprovalType.TRANSFER)) {
if (activeTaskList.size() > 1) {
errorMessage = "数据验证失败转办任务数量不能多于1个";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
// 如果是转办任务,无需节点跳转,将指派人改为当前用户即可。
Task task = activeTaskList.get(0);
task.setAssignee(TokenData.takeFromRequest().getLoginName());
} else {
CallResult result =
flowApiService.backToRuntimeTask(activeTaskList.get(0), null, false, taskComment);
if (!result.isSuccess()) {
return ResponseResult.errorFrom(result);
}
}
return ResponseResult.success();
}
/**
* 获取当前流程任务的审批列表。
*
* @param processInstanceId 当前运行时的流程实例Id。
* @return 当前流程实例的详情数据。
*/
@GetMapping("/listFlowTaskComment")
public ResponseResult<List<FlowTaskCommentVo>> listFlowTaskComment(@RequestParam String processInstanceId) {
List<FlowTaskComment> flowTaskCommentList =
flowTaskCommentService.getFlowTaskCommentList(processInstanceId);
List<FlowTaskCommentVo> resultList = MyModelUtil.copyCollectionTo(flowTaskCommentList, FlowTaskCommentVo.class);
return ResponseResult.success(resultList);
}
/**
* 获取指定流程定义的流程图。
*
* @param processDefinitionId 流程定义Id。
* @return 流程图。
*/
@GetMapping("/viewProcessBpmn")
public ResponseResult<String> viewProcessBpmn(@RequestParam String processDefinitionId) throws IOException {
BpmnXMLConverter converter = new BpmnXMLConverter();
BpmnModel bpmnModel = flowApiService.getBpmnModelByDefinitionId(processDefinitionId);
byte[] xmlBytes = converter.convertToXML(bpmnModel);
InputStream in = new ByteArrayInputStream(xmlBytes);
return ResponseResult.success(StreamUtils.copyToString(in, StandardCharsets.UTF_8));
}
/**
* 获取流程图高亮数据。
*
* @param processInstanceId 流程实例Id。
* @return 流程图高亮数据。
*/
@GetMapping("/viewHighlightFlowData")
public ResponseResult<JSONObject> viewHighlightFlowData(@RequestParam String processInstanceId) {
List<HistoricActivityInstance> activityInstanceList =
flowApiService.getHistoricActivityInstanceList(processInstanceId);
Set<String> finishedTaskSet = activityInstanceList.stream()
.filter(s -> !StrUtil.equals(s.getActivityType(), "sequenceFlow"))
.map(HistoricActivityInstance::getActivityId).collect(Collectors.toSet());
Set<String> finishedSequenceFlowSet = activityInstanceList.stream()
.filter(s -> StrUtil.equals(s.getActivityType(), "sequenceFlow"))
.map(HistoricActivityInstance::getActivityId).collect(Collectors.toSet());
//获取流程实例当前正在待办的节点
List<HistoricActivityInstance> unfinishedInstanceList =
flowApiService.getHistoricUnfinishedInstanceList(processInstanceId);
Set<String> unfinishedTaskSet = new LinkedHashSet<>();
for (HistoricActivityInstance unfinishedActivity : unfinishedInstanceList) {
unfinishedTaskSet.add(unfinishedActivity.getActivityId());
}
JSONObject jsonData = new JSONObject();
jsonData.put("finishedTaskSet", finishedTaskSet);
jsonData.put("finishedSequenceFlowSet", finishedSequenceFlowSet);
jsonData.put("unfinishedTaskSet", unfinishedTaskSet);
return ResponseResult.success(jsonData);
}
/**
* 获取当前用户的已办理的审批任务列表。
*
* @param processDefinitionName 流程名。
* @param beginDate 流程发起开始时间。
* @param endDate 流程发起结束时间。
* @param pageParam 分页对象。
* @return 查询结果应答。
*/
@DisableDataFilter
@PostMapping("/listHistoricTask")
public ResponseResult<MyPageData<Map<String, Object>>> listHistoricTask(
@MyRequestBody String processDefinitionName,
@MyRequestBody String beginDate,
@MyRequestBody String endDate,
@MyRequestBody(required = true) MyPageParam pageParam) throws ParseException {
MyPageData<HistoricTaskInstance> pageData =
flowApiService.getHistoricTaskInstanceFinishedList(processDefinitionName, beginDate, endDate, pageParam);
List<Map<String, Object>> resultList = new LinkedList<>();
pageData.getDataList().forEach(instance -> resultList.add(BeanUtil.beanToMap(instance)));
List<HistoricTaskInstance> taskInstanceList = pageData.getDataList();
if (CollUtil.isNotEmpty(taskInstanceList)) {
Set<String> instanceIdSet = taskInstanceList.stream()
.map(HistoricTaskInstance::getProcessInstanceId).collect(Collectors.toSet());
List<HistoricProcessInstance> instanceList = flowApiService.getHistoricProcessInstanceList(instanceIdSet);
Set<String> loginNameSet = instanceList.stream()
.map(HistoricProcessInstance::getStartUserId).collect(Collectors.toSet());
List<FlowUserInfoVo> userInfoList = flowCustomExtFactory
.getFlowIdentityExtHelper().getUserInfoListByUsernameSet(loginNameSet);
Map<String, FlowUserInfoVo> userInfoMap =
userInfoList.stream().collect(Collectors.toMap(FlowUserInfoVo::getLoginName, c -> c));
Map<String, HistoricProcessInstance> instanceMap =
instanceList.stream().collect(Collectors.toMap(HistoricProcessInstance::getId, c -> c));
List<FlowWorkOrder> workOrderList =
flowWorkOrderService.getInList(INSTANCE_ID, instanceIdSet);
Map<String, FlowWorkOrder> workOrderMap =
workOrderList.stream().collect(Collectors.toMap(FlowWorkOrder::getProcessInstanceId, c -> c));
resultList.forEach(result -> {
String instanceId = result.get(INSTANCE_ID).toString();
HistoricProcessInstance instance = instanceMap.get(instanceId);
result.put("processDefinitionKey", instance.getProcessDefinitionKey());
result.put("processDefinitionName", instance.getProcessDefinitionName());
result.put("startUser", instance.getStartUserId());
FlowUserInfoVo userInfo = userInfoMap.get(instance.getStartUserId());
result.put(SHOW_NAME, userInfo.getShowName());
result.put("headImageUrl", userInfo.getHeadImageUrl());
result.put("businessKey", instance.getBusinessKey());
FlowWorkOrder flowWorkOrder = workOrderMap.get(instanceId);
if (flowWorkOrder != null) {
result.put("workOrderCode", flowWorkOrder.getWorkOrderCode());
}
});
Set<String> taskIdSet =
taskInstanceList.stream().map(HistoricTaskInstance::getId).collect(Collectors.toSet());
List<FlowTaskComment> commentList = flowTaskCommentService.getFlowTaskCommentListByTaskIds(taskIdSet);
Map<String, List<FlowTaskComment>> commentMap =
commentList.stream().collect(Collectors.groupingBy(FlowTaskComment::getTaskId));
resultList.forEach(result -> {
List<FlowTaskComment> comments = commentMap.get(result.get("id").toString());
if (CollUtil.isNotEmpty(comments)) {
result.put("approvalType", comments.get(0).getApprovalType());
comments.remove(0);
}
});
}
return ResponseResult.success(MyPageUtil.makeResponseData(resultList, pageData.getTotalCount()));
}
/**
* 根据输入参数查询,当前用户的历史流程数据。
*
* @param processDefinitionName 流程名。
* @param beginDate 流程发起开始时间。
* @param endDate 流程发起结束时间。
* @param pageParam 分页对象。
* @return 查询结果应答。
*/
@DisableDataFilter
@PostMapping("/listHistoricProcessInstance")
public ResponseResult<MyPageData<Map<String, Object>>> listHistoricProcessInstance(
@MyRequestBody String processDefinitionName,
@MyRequestBody String beginDate,
@MyRequestBody String endDate,
@MyRequestBody(required = true) MyPageParam pageParam) throws ParseException {
String loginName = TokenData.takeFromRequest().getLoginName();
MyPageData<HistoricProcessInstance> pageData = flowApiService.getHistoricProcessInstanceList(
null, processDefinitionName, loginName, beginDate, endDate, pageParam, true);
Set<String> loginNameSet = pageData.getDataList().stream()
.map(HistoricProcessInstance::getStartUserId).collect(Collectors.toSet());
List<FlowUserInfoVo> userInfoList = flowCustomExtFactory
.getFlowIdentityExtHelper().getUserInfoListByUsernameSet(loginNameSet);
if (CollUtil.isEmpty(userInfoList)) {
userInfoList = new LinkedList<>();
}
Map<String, FlowUserInfoVo> userInfoMap =
userInfoList.stream().collect(Collectors.toMap(FlowUserInfoVo::getLoginName, c -> c));
Set<String> instanceIdSet = pageData.getDataList().stream()
.map(HistoricProcessInstance::getId).collect(Collectors.toSet());
List<FlowWorkOrder> workOrderList =
flowWorkOrderService.getInList(INSTANCE_ID, instanceIdSet);
Map<String, FlowWorkOrder> workOrderMap =
workOrderList.stream().collect(Collectors.toMap(FlowWorkOrder::getProcessInstanceId, c -> c));
List<Map<String, Object>> resultList = new LinkedList<>();
pageData.getDataList().forEach(instance -> {
Map<String, Object> data = BeanUtil.beanToMap(instance);
FlowUserInfoVo userInfo = userInfoMap.get(instance.getStartUserId());
if (userInfo != null) {
data.put(SHOW_NAME, userInfo.getShowName());
data.put("headImageUrl", userInfo.getHeadImageUrl());
}
FlowWorkOrder workOrder = workOrderMap.get(instance.getId());
if (workOrder != null) {
data.put("workOrderCode", workOrder.getWorkOrderCode());
data.put("flowStatus", workOrder.getFlowStatus());
}
resultList.add(data);
});
return ResponseResult.success(MyPageUtil.makeResponseData(resultList, pageData.getTotalCount()));
}
/**
* 根据输入参数查询,所有历史流程数据。
*
* @param processDefinitionName 流程名。
* @param startUser 流程发起用户。
* @param beginDate 流程发起开始时间。
* @param endDate 流程发起结束时间。
* @param pageParam 分页对象。
* @return 查询结果。
*/
@PostMapping("/listAllHistoricProcessInstance")
public ResponseResult<MyPageData<Map<String, Object>>> listAllHistoricProcessInstance(
@MyRequestBody String processDefinitionName,
@MyRequestBody String startUser,
@MyRequestBody String beginDate,
@MyRequestBody String endDate,
@MyRequestBody(required = true) MyPageParam pageParam) throws ParseException {
MyPageData<HistoricProcessInstance> pageData = flowApiService.getHistoricProcessInstanceList(
null, processDefinitionName, startUser, beginDate, endDate, pageParam, false);
List<Map<String, Object>> resultList = new LinkedList<>();
pageData.getDataList().forEach(instance -> resultList.add(BeanUtil.beanToMap(instance)));
List<String> unfinishedProcessInstanceIds = pageData.getDataList().stream()
.filter(c -> c.getEndTime() == null)
.map(HistoricProcessInstance::getId)
.collect(Collectors.toList());
MyPageData<Map<String, Object>> pageResultData =
MyPageUtil.makeResponseData(resultList, pageData.getTotalCount());
if (CollUtil.isEmpty(unfinishedProcessInstanceIds)) {
return ResponseResult.success(pageResultData);
}
Set<String> processInstanceIds = pageData.getDataList().stream()
.map(HistoricProcessInstance::getId).collect(Collectors.toSet());
List<Task> taskList = flowApiService.getTaskListByProcessInstanceIds(unfinishedProcessInstanceIds);
Map<String, List<Task>> taskMap =
taskList.stream().collect(Collectors.groupingBy(Task::getProcessInstanceId));
for (Map<String, Object> result : resultList) {
String processInstanceId = result.get(INSTANCE_ID).toString();
List<Task> instanceTaskList = taskMap.get(processInstanceId);
if (instanceTaskList != null) {
JSONArray taskArray = new JSONArray();
for (Task task : instanceTaskList) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("taskId", task.getId());
jsonObject.put("taskName", task.getName());
jsonObject.put("taskKey", task.getTaskDefinitionKey());
jsonObject.put("assignee", task.getAssignee());
taskArray.add(jsonObject);
}
result.put("runtimeTaskInfoList", taskArray);
}
}
return ResponseResult.success(pageResultData);
}
/**
* 催办工单,只有流程发起人才可以催办工单。
* 催办场景必须要取消数据权限过滤,因为流程的指派很可能是跨越部门的。
* 既然被指派和催办了,这里就应该禁用工单表的数据权限过滤约束。
* 如果您的系统没有支持数据权限过滤DisableDataFilter不会有任何影响建议保留。
*
* @param workOrderId 工单Id。
* @return 应答结果。
*/
@DisableDataFilter
@OperationLog(type = SysOperationLogType.REMIND_TASK)
@PostMapping("/remindRuntimeTask")
public ResponseResult<Void> remindRuntimeTask(@MyRequestBody(required = true) Long workOrderId) {
FlowWorkOrder flowWorkOrder = flowWorkOrderService.getById(workOrderId);
if (flowWorkOrder == null) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
}
String errorMessage;
if (!flowWorkOrder.getCreateUserId().equals(TokenData.takeFromRequest().getUserId())) {
errorMessage = "数据验证失败,只有流程发起人才能催办工单!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
if (flowWorkOrder.getFlowStatus().equals(FlowTaskStatus.FINISHED)
|| flowWorkOrder.getFlowStatus().equals(FlowTaskStatus.CANCELLED)
|| flowWorkOrder.getFlowStatus().equals(FlowTaskStatus.STOPPED)) {
errorMessage = "数据验证失败,已经结束的流程,不能催办工单!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
if (flowWorkOrder.getFlowStatus().equals(FlowTaskStatus.DRAFT)) {
errorMessage = "数据验证失败,流程草稿不能催办工单!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
flowMessageService.saveNewRemindMessage(flowWorkOrder);
return ResponseResult.success();
}
/**
* 取消工作流工单,仅当没有进入任何审批流程之前,才可以取消工单。
*
* @param workOrderId 工单Id。
* @param cancelReason 取消原因。
* @return 应答结果。
*/
@OperationLog(type = SysOperationLogType.CANCEL_FLOW)
@DisableDataFilter
@PostMapping("/cancelWorkOrder")
public ResponseResult<Void> cancelWorkOrder(
@MyRequestBody(required = true) Long workOrderId,
@MyRequestBody(required = true) String cancelReason) {
FlowWorkOrder flowWorkOrder = flowWorkOrderService.getById(workOrderId);
if (flowWorkOrder == null) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
}
String errorMessage;
if (!flowWorkOrder.getFlowStatus().equals(FlowTaskStatus.SUBMITTED)
&& !flowWorkOrder.getFlowStatus().equals(FlowTaskStatus.DRAFT)) {
errorMessage = "数据验证失败,当前流程已经进入审批状态,不能撤销工单!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
if (!flowWorkOrder.getCreateUserId().equals(TokenData.takeFromRequest().getUserId())) {
errorMessage = "数据验证失败,当前用户不是工单所有者,不能撤销工单!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
CallResult result;
// 草稿工单直接删除当前工单。
if (flowWorkOrder.getFlowStatus().equals(FlowTaskStatus.DRAFT)) {
result = flowWorkOrderService.removeDraft(flowWorkOrder);
} else {
result = flowApiService.stopProcessInstance(
flowWorkOrder.getProcessInstanceId(), cancelReason, true);
}
if (!result.isSuccess()) {
return ResponseResult.errorFrom(result);
}
return ResponseResult.success();
}
/**
* 获取指定流程定义Id的所有用户任务数据列表。
*
* @param processDefinitionId 流程定义Id。
* @return 查询结果。
*/
@GetMapping("/listAllUserTask")
public ResponseResult<List<JSONObject>> listAllUserTask(@RequestParam String processDefinitionId) {
Map<String, UserTask> taskMap = flowApiService.getAllUserTaskMap(processDefinitionId);
List<JSONObject> resultList = new LinkedList<>();
for (UserTask t : taskMap.values()) {
JSONObject data = new JSONObject();
data.put("id", t.getId());
data.put("name", t.getName());
resultList.add(data);
}
return ResponseResult.success(resultList);
}
/**
* 终止流程实例,将任务从当前节点直接流转到主流程的结束事件。
*
* @param processInstanceId 流程实例Id。
* @param stopReason 停止原因。
* @return 执行结果应答。
*/
@SaCheckPermission("flowOperation.all")
@OperationLog(type = SysOperationLogType.STOP_FLOW)
@DisableDataFilter
@PostMapping("/stopProcessInstance")
public ResponseResult<Void> stopProcessInstance(
@MyRequestBody(required = true) String processInstanceId,
@MyRequestBody(required = true) String stopReason) {
CallResult result = flowApiService.stopProcessInstance(processInstanceId, stopReason, false);
if (!result.isSuccess()) {
return ResponseResult.errorFrom(result);
}
return ResponseResult.success();
}
/**
* 删除流程实例。
*
* @param processInstanceId 流程实例Id。
* @return 执行结果应答。
*/
@SaCheckPermission("flowOperation.all")
@OperationLog(type = SysOperationLogType.DELETE_FLOW)
@PostMapping("/deleteProcessInstance")
public ResponseResult<Void> deleteProcessInstance(@MyRequestBody(required = true) String processInstanceId) {
flowApiService.deleteProcessInstance(processInstanceId);
return ResponseResult.success();
}
private List<FlowTaskComment> buildApprovedFlowTaskCommentList(TaskInfo taskInfo, boolean isMultiInstanceTask) {
List<FlowTaskComment> taskCommentList;
if (isMultiInstanceTask) {
String multiInstanceExecId;
FlowMultiInstanceTrans trans =
flowMultiInstanceTransService.getByExecutionId(taskInfo.getExecutionId(), taskInfo.getId());
if (trans != null) {
multiInstanceExecId = trans.getMultiInstanceExecId();
} else {
multiInstanceExecId = flowApiService.getExecutionVariableStringWithSafe(
taskInfo.getExecutionId(), FlowConstant.MULTI_SIGN_TASK_EXECUTION_ID_VAR);
}
taskCommentList = flowTaskCommentService.getFlowTaskCommentListByMultiInstanceExecId(multiInstanceExecId);
} else {
taskCommentList = flowTaskCommentService.getFlowTaskCommentListByExecutionId(
taskInfo.getProcessInstanceId(), taskInfo.getId(), taskInfo.getExecutionId());
}
return taskCommentList;
}
private ResponseResult<JSONObject> doVerifyMultiSign(String processInstanceId, String taskId) {
String errorMessage;
if (!flowApiService.existActiveProcessInstance(processInstanceId)) {
errorMessage = "数据验证失败,当前流程实例已经结束,不能执行加签!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
HistoricTaskInstance taskInstance = flowApiService.getHistoricTaskInstance(processInstanceId, taskId);
if (taskInstance == null) {
errorMessage = "数据验证失败,当前任务不存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
String loginName = TokenData.takeFromRequest().getLoginName();
if (!StrUtil.equals(taskInstance.getAssignee(), loginName)) {
errorMessage = "数据验证失败,任务指派人与当前用户不匹配!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
List<Task> activeTaskList = flowApiService.getProcessInstanceActiveTaskList(processInstanceId);
Task activeMultiInstanceTask = null;
Map<String, UserTask> userTaskMap = flowApiService.getAllUserTaskMap(taskInstance.getProcessDefinitionId());
for (Task activeTask : activeTaskList) {
UserTask userTask = userTaskMap.get(activeTask.getTaskDefinitionKey());
if (!userTask.hasMultiInstanceLoopCharacteristics()) {
errorMessage = "数据验证失败,指定加签任务不存在或已审批完毕!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
String startTaskId = flowApiService.getTaskVariableStringWithSafe(
activeTask.getId(), FlowConstant.MULTI_SIGN_START_TASK_VAR);
if (StrUtil.equals(startTaskId, taskId)) {
activeMultiInstanceTask = activeTask;
break;
}
}
if (activeMultiInstanceTask == null) {
errorMessage = "数据验证失败,指定加签任务不存在或已审批完毕!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
JSONObject resultData = new JSONObject();
resultData.put("taskInstance", taskInstance);
resultData.put(ACTIVE_MULTI_INST_TASK, activeMultiInstanceTask);
return ResponseResult.success(resultData);
}
private String findExistAssignee(Set<String> assigneeSet, JSONArray assigneeArray) {
for (int i = 0; i < assigneeArray.size(); i++) {
String loginName = assigneeArray.getString(i);
if (assigneeSet.contains(loginName)) {
return loginName;
}
}
return null;
}
}

View File

@@ -0,0 +1,26 @@
package com.orangeforms.common.flow.dao;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.flow.model.FlowCategory;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* FlowCategory数据操作访问接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowCategoryMapper extends BaseDaoMapper<FlowCategory> {
/**
* 获取过滤后的对象列表。
*
* @param flowCategoryFilter 主表过滤对象。
* @param orderBy 排序字符串order by从句的参数。
* @return 对象列表。
*/
List<FlowCategory> getFlowCategoryList(
@Param("flowCategoryFilter") FlowCategory flowCategoryFilter, @Param("orderBy") String orderBy);
}

View File

@@ -0,0 +1,26 @@
package com.orangeforms.common.flow.dao;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.flow.model.FlowEntry;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* FlowEntry数据操作访问接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowEntryMapper extends BaseDaoMapper<FlowEntry> {
/**
* 获取过滤后的对象列表。
*
* @param flowEntryFilter 主表过滤对象。
* @param orderBy 排序字符串order by从句的参数。
* @return 对象列表。
*/
List<FlowEntry> getFlowEntryList(
@Param("flowEntryFilter") FlowEntry flowEntryFilter, @Param("orderBy") String orderBy);
}

View File

@@ -0,0 +1,13 @@
package com.orangeforms.common.flow.dao;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.flow.model.FlowEntryPublish;
/**
* 数据操作访问接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowEntryPublishMapper extends BaseDaoMapper<FlowEntryPublish> {
}

View File

@@ -0,0 +1,22 @@
package com.orangeforms.common.flow.dao;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.flow.model.FlowEntryPublishVariable;
import java.util.List;
/**
* 数据操作访问接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowEntryPublishVariableMapper extends BaseDaoMapper<FlowEntryPublishVariable> {
/**
* 批量插入流程发布的变量列表。
*
* @param entryPublishVariableList 流程发布的变量列表。
*/
void insertList(List<FlowEntryPublishVariable> entryPublishVariableList);
}

View File

@@ -0,0 +1,27 @@
package com.orangeforms.common.flow.dao;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.flow.model.FlowEntryVariable;
import org.apache.ibatis.annotations.Param;
import java.util.*;
/**
* 流程变量数据操作访问接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowEntryVariableMapper extends BaseDaoMapper<FlowEntryVariable> {
/**
* 获取过滤后的对象列表。
*
* @param flowEntryVariableFilter 主表过滤对象。
* @param orderBy 排序字符串order by从句的参数。
* @return 对象列表。
*/
List<FlowEntryVariable> getFlowEntryVariableList(
@Param("flowEntryVariableFilter") FlowEntryVariable flowEntryVariableFilter,
@Param("orderBy") String orderBy);
}

View File

@@ -0,0 +1,21 @@
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;
/**
* 流程任务消息的候选身份数据操作访问接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowMessageCandidateIdentityMapper extends BaseDaoMapper<FlowMessageCandidateIdentity> {
/**
* 删除指定流程实例的消息关联数据。
*
* @param processInstanceId 流程实例Id。
*/
void deleteByProcessInstanceId(@Param("processInstanceId") String processInstanceId);
}

View File

@@ -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 2024-07-02
*/
public interface FlowMessageIdentityOperationMapper extends BaseDaoMapper<FlowMessageIdentityOperation> {
/**
* 删除指定流程实例的消息关联数据。
*
* @param processInstanceId 流程实例Id。
*/
void deleteByProcessInstanceId(@Param("processInstanceId") String processInstanceId);
}

View File

@@ -0,0 +1,79 @@
package com.orangeforms.common.flow.dao;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.flow.model.FlowMessage;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Set;
/**
* 工作流消息数据操作访问接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowMessageMapper extends BaseDaoMapper<FlowMessage> {
/**
* 获取指定用户和身份分组Id集合的催办消息列表。
*
* @param tenantId 租户Id。
* @param appCode 应用编码。
* @param loginName 用户的登录名。与流程任务的assignee精确匹配。
* @param groupIdSet 用户身份分组Id集合。
* @return 查询后的催办消息列表。
*/
List<FlowMessage> getRemindingMessageListByUser(
@Param("tenantId") Long tenantId,
@Param("appCode") String appCode,
@Param("loginName") String loginName,
@Param("groupIdSet") Set<String> groupIdSet);
/**
* 获取指定用户和身份分组Id集合的抄送消息列表。
*
* @param tenantId 租户Id。
* @param appCode 应用编码。
* @param loginName 用户登录名。
* @param groupIdSet 用户身份分组Id集合。
* @param read true表示已读false表示未读。
* @return 查询后的抄送消息列表。
*/
List<FlowMessage> getCopyMessageListByUser(
@Param("tenantId") Long tenantId,
@Param("appCode") String appCode,
@Param("loginName") String loginName,
@Param("groupIdSet") Set<String> groupIdSet,
@Param("read") Boolean read);
/**
* 计算当前用户催办消息的数量。
*
* @param tenantId 租户Id。
* @param appCode 应用编码。
* @param loginName 用户登录名。
* @param groupIdSet 用户身份分组Id集合。
* @return 数据数量。
*/
int countRemindingMessageListByUser(
@Param("tenantId") Long tenantId,
@Param("appCode") String appCode,
@Param("loginName") String loginName,
@Param("groupIdSet") Set<String> groupIdSet);
/**
* 计算当前用户未读抄送消息的数量。
*
* @param tenantId 租户Id。
* @param appCode 应用编码。
* @param loginName 用户登录名。
* @param groupIdSet 用户身份分组Id集合。
* @return 数据数量
*/
int countCopyMessageListByUser(
@Param("tenantId") Long tenantId,
@Param("appCode") String appCode,
@Param("loginName") String loginName,
@Param("groupIdSet") Set<String> groupIdSet);
}

View File

@@ -0,0 +1,13 @@
package com.orangeforms.common.flow.dao;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.flow.model.FlowMultiInstanceTrans;
/**
* 流程多实例任务执行流水访问接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowMultiInstanceTransMapper extends BaseDaoMapper<FlowMultiInstanceTrans> {
}

View File

@@ -0,0 +1,13 @@
package com.orangeforms.common.flow.dao;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.flow.model.FlowTaskComment;
/**
* 流程任务批注数据操作访问接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowTaskCommentMapper extends BaseDaoMapper<FlowTaskComment> {
}

View File

@@ -0,0 +1,22 @@
package com.orangeforms.common.flow.dao;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.flow.model.FlowTaskExt;
import java.util.List;
/**
* 流程任务扩展数据操作访问接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowTaskExtMapper extends BaseDaoMapper<FlowTaskExt> {
/**
* 批量插入流程任务扩展信息列表。
*
* @param flowTaskExtList 流程任务扩展信息列表。
*/
void insertList(List<FlowTaskExt> flowTaskExtList);
}

View File

@@ -0,0 +1,14 @@
package com.orangeforms.common.flow.dao;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.flow.model.FlowWorkOrderExt;
/**
* 工作流工单扩展数据操作访问接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowWorkOrderExtMapper extends BaseDaoMapper<FlowWorkOrderExt> {
}

View File

@@ -0,0 +1,49 @@
package com.orangeforms.common.flow.dao;
import com.orangeforms.common.core.annotation.EnableDataPerm;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.flow.model.FlowWorkOrder;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.*;
/**
* 工作流工单表数据操作访问接口。
* 如果当前系统支持数据权限过滤当前用户必须要能看自己的工单数据所以需要把EnableDataPerm
* 的mustIncludeUserRule参数设置为true即便当前用户的数据权限中并不包含DataPermRuleType.TYPE_USER_ONLY
* 数据过滤拦截组件也会自动补偿该类型的数据权限,以便当前用户可以看到自己发起的工单。
*
* @author Jerry
* @date 2024-07-02
*/
@EnableDataPerm(mustIncludeUserRule = true)
public interface FlowWorkOrderMapper extends BaseDaoMapper<FlowWorkOrder> {
/**
* 获取过滤后的对象列表。
*
* @param flowWorkOrderFilter 主表过滤对象。
* @param orderBy 排序字符串order by从句的参数。
* @return 对象列表。
*/
List<FlowWorkOrder> getFlowWorkOrderList(
@Param("flowWorkOrderFilter") FlowWorkOrder flowWorkOrderFilter, @Param("orderBy") String orderBy);
/**
* 计算指定前缀工单编码的最大值。
*
* @param prefix 工单编码前缀。
* @return 该工单编码前缀的最大值。
*/
@Select("SELECT MAX(work_order_code) FROM zz_flow_work_order WHERE work_order_code LIKE '${prefix}'")
String getMaxWorkOrderCodeByPrefix(@Param("prefix") String prefix);
/**
* 根据工单编码查询指定工单,查询过程也会考虑逻辑删除的数据。
* @param workOrderCode 工单编码。
* @return 工单编码的流程工单数量。
*/
@Select("SELECT COUNT(*) FROM zz_flow_work_order WHERE work_order_code = #{workOrderCode}")
int getCountByWorkOrderCode(@Param("workOrderCode") String workOrderCode);
}

View File

@@ -0,0 +1,56 @@
<?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.FlowCategoryMapper">
<resultMap id="BaseResultMap" type="com.orangeforms.common.flow.model.FlowCategory">
<id column="category_id" jdbcType="BIGINT" property="categoryId"/>
<result column="tenant_id" jdbcType="BIGINT" property="tenantId"/>
<result column="app_code" jdbcType="VARCHAR" property="appCode"/>
<result column="name" jdbcType="VARCHAR" property="name"/>
<result column="code" jdbcType="VARCHAR" property="code"/>
<result column="show_order" jdbcType="INTEGER" property="showOrder"/>
<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"/>
<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.tenantId == null">
AND zz_flow_category.tenant_id IS NULL
</if>
<if test="flowCategoryFilter.tenantId != null">
AND zz_flow_category.tenant_id = #{flowCategoryFilter.tenantId}
</if>
<if test="flowCategoryFilter.appCode == null">
AND zz_flow_category.app_code IS NULL
</if>
<if test="flowCategoryFilter.appCode != null">
AND zz_flow_category.app_code = #{flowCategoryFilter.appCode}
</if>
<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>
</select>
</mapper>

View File

@@ -0,0 +1,94 @@
<?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.FlowEntryMapper">
<resultMap id="BaseResultMap" type="com.orangeforms.common.flow.model.FlowEntry">
<id column="entry_id" jdbcType="BIGINT" property="entryId"/>
<result column="tenant_id" jdbcType="BIGINT" property="tenantId"/>
<result column="app_code" jdbcType="VARCHAR" property="appCode"/>
<result column="process_definition_name" jdbcType="VARCHAR" property="processDefinitionName"/>
<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="latest_publish_time" jdbcType="TIMESTAMP" property="latestPublishTime"/>
<result column="status" jdbcType="INTEGER" property="status"/>
<result column="bpmn_xml" jdbcType="LONGVARCHAR" property="bpmnXml"/>
<result column="diagram_type" jdbcType="INTEGER" property="diagramType"/>
<result column="bind_form_type" jdbcType="INTEGER" property="bindFormType"/>
<result column="page_id" jdbcType="BIGINT" property="pageId"/>
<result column="default_form_id" jdbcType="BIGINT" property="defaultFormId"/>
<result column="default_router_name" jdbcType="VARCHAR" property="defaultRouterName"/>
<result column="encoded_rule" jdbcType="VARCHAR" property="encodedRule"/>
<result column="extension_data" jdbcType="VARCHAR" property="extensionData"/>
<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"/>
<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.FlowEntryMapper.inputFilterRef"/>
</sql>
<!-- 这里仅包含调用接口输入的主表过滤条件 -->
<sql id="inputFilterRef">
<if test="flowEntryFilter != null">
<if test="flowEntryFilter.tenantId == null">
AND zz_flow_entry.tenant_id IS NULL
</if>
<if test="flowEntryFilter.tenantId != null">
AND zz_flow_entry.tenant_id = #{flowEntryFilter.tenantId}
</if>
<if test="flowEntryFilter.appCode == null">
AND zz_flow_entry.app_code IS NULL
</if>
<if test="flowEntryFilter.appCode != null">
AND zz_flow_entry.app_code = #{flowEntryFilter.appCode}
</if>
<if test="flowEntryFilter.processDefinitionName != null and flowEntryFilter.processDefinitionName != ''">
AND zz_flow_entry.process_definition_name = #{flowEntryFilter.processDefinitionName}
</if>
<if test="flowEntryFilter.processDefinitionKey != null and flowEntryFilter.processDefinitionKey != ''">
AND zz_flow_entry.process_definition_key = #{flowEntryFilter.processDefinitionKey}
</if>
<if test="flowEntryFilter.categoryId != null">
AND zz_flow_entry.category_id = #{flowEntryFilter.categoryId}
</if>
<if test="flowEntryFilter.status != null">
AND zz_flow_entry.status = #{flowEntryFilter.status}
</if>
</if>
</sql>
<select id="getFlowEntryList" resultMap="BaseResultMap" parameterType="com.orangeforms.common.flow.model.FlowEntry">
SELECT
entry_id,
app_code,
process_definition_name,
process_definition_key,
category_id,
main_entry_publish_id,
latest_publish_time,
status,
diagram_type,
bind_form_type,
page_id,
default_form_id,
default_router_name,
encoded_rule,
extension_data,
update_time,
update_user_id,
create_time,
create_user_id
FROM
zz_flow_entry
<where>
<include refid="filterRef"/>
</where>
<if test="orderBy != null and orderBy != ''">
ORDER BY ${orderBy}
</if>
</select>
</mapper>

View File

@@ -0,0 +1,18 @@
<?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.FlowEntryPublishMapper">
<resultMap id="BaseResultMap" type="com.orangeforms.common.flow.model.FlowEntryPublish">
<id column="entry_publish_id" jdbcType="BIGINT" property="entryPublishId"/>
<result column="entry_id" jdbcType="BIGINT" property="entryId"/>
<result column="deploy_id" jdbcType="VARCHAR" property="deployId"/>
<result column="process_definition_id" jdbcType="VARCHAR" property="processDefinitionId"/>
<result column="publish_version" jdbcType="INTEGER" property="publishVersion"/>
<result column="active_status" jdbcType="BOOLEAN" property="activeStatus"/>
<result column="main_version" jdbcType="BOOLEAN" property="mainVersion"/>
<result column="create_user_id" jdbcType="BIGINT" property="createUserId"/>
<result column="publish_time" jdbcType="TIMESTAMP" property="publishTime"/>
<result column="init_task_info" jdbcType="VARCHAR" property="initTaskInfo"/>
<result column="analyzed_node_json" jdbcType="VARCHAR" property="analyzedNodeJson"/>
<result column="extension_data" jdbcType="VARCHAR" property="extensionData"/>
</resultMap>
</mapper>

View File

@@ -0,0 +1,30 @@
<?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.FlowEntryPublishVariableMapper">
<resultMap id="BaseResultMap" type="com.orangeforms.common.flow.model.FlowEntryPublishVariable">
<id column="variable_id" jdbcType="BIGINT" property="variableId"/>
<result column="entry_publish_id" jdbcType="BIGINT" property="entryPublishId"/>
<result column="variable_name" jdbcType="VARCHAR" property="variableName"/>
<result column="show_name" jdbcType="VARCHAR" property="showName"/>
<result column="variable_type" jdbcType="INTEGER" property="variableType"/>
<result column="bind_datasource_id" jdbcType="BIGINT" property="bindDatasourceId"/>
<result column="bind_relation_id" jdbcType="BIGINT" property="bindRelationId"/>
<result column="bind_column_id" jdbcType="BIGINT" property="bindColumnId"/>
<result column="builtin" jdbcType="BOOLEAN" property="builtin"/>
</resultMap>
<insert id="insertList">
INSERT INTO zz_flow_entry_publish_variable VALUES
<foreach collection="list" index="index" item="item" separator="," >
(#{item.variableId},
#{item.entryPublishId},
#{item.variableName},
#{item.showName},
#{item.variableType},
#{item.bindDatasourceId},
#{item.bindRelationId},
#{item.bindColumnId},
#{item.builtin})
</foreach>
</insert>
</mapper>

View File

@@ -0,0 +1,41 @@
<?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.FlowEntryVariableMapper">
<resultMap id="BaseResultMap" type="com.orangeforms.common.flow.model.FlowEntryVariable">
<id column="variable_id" jdbcType="BIGINT" property="variableId"/>
<result column="entry_id" jdbcType="BIGINT" property="entryId"/>
<result column="variable_name" jdbcType="VARCHAR" property="variableName"/>
<result column="show_name" jdbcType="VARCHAR" property="showName"/>
<result column="variable_type" jdbcType="INTEGER" property="variableType"/>
<result column="bind_datasource_id" jdbcType="BIGINT" property="bindDatasourceId"/>
<result column="bind_relation_id" jdbcType="BIGINT" property="bindRelationId"/>
<result column="bind_column_id" jdbcType="BIGINT" property="bindColumnId"/>
<result column="builtin" jdbcType="BOOLEAN" property="builtin"/>
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
</resultMap>
<!-- 如果有逻辑删除字段过滤,请写到这里 -->
<sql id="filterRef">
<!-- 这里必须加上全包名否则当filterRef被其他Mapper.xml包含引用的时候就会调用Mapper.xml中的该SQL片段 -->
<include refid="com.orangeforms.common.flow.dao.FlowEntryVariableMapper.inputFilterRef"/>
</sql>
<!-- 这里仅包含调用接口输入的主表过滤条件 -->
<sql id="inputFilterRef">
<if test="flowEntryVariableFilter != null">
<if test="flowEntryVariableFilter.entryId != null">
AND zz_flow_entry_variable.entry_id = #{flowEntryVariableFilter.entryId}
</if>
</if>
</sql>
<select id="getFlowEntryVariableList" resultMap="BaseResultMap" parameterType="com.orangeforms.common.flow.model.FlowEntryVariable">
SELECT * FROM zz_flow_entry_variable
<where>
<include refid="filterRef"/>
</where>
<if test="orderBy != null and orderBy != ''">
ORDER BY ${orderBy}
</if>
</select>
</mapper>

View File

@@ -0,0 +1,16 @@
<?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.FlowMessageCandidateIdentityMapper">
<resultMap id="BaseResultMap" type="com.orangeforms.common.flow.model.FlowMessageCandidateIdentity">
<id column="id" jdbcType="BIGINT" property="id"/>
<result column="message_id" jdbcType="BIGINT" property="messageId"/>
<result column="candidate_type" jdbcType="VARCHAR" property="candidateType"/>
<result column="candidate_id" jdbcType="VARCHAR" property="candidateId"/>
</resultMap>
<delete id="deleteByProcessInstanceId">
DELETE FROM zz_flow_msg_candidate_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>

View File

@@ -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_msg_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>

View File

@@ -0,0 +1,112 @@
<?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.FlowMessageMapper">
<resultMap id="BaseResultMap" type="com.orangeforms.common.flow.model.FlowMessage">
<id column="message_id" jdbcType="BIGINT" property="messageId"/>
<result column="tenant_id" jdbcType="BIGINT" property="tenantId"/>
<result column="app_code" jdbcType="VARCHAR" property="appCode"/>
<result column="message_type" jdbcType="TINYINT" property="messageType"/>
<result column="message_content" jdbcType="VARCHAR" property="messageContent"/>
<result column="remind_count" jdbcType="INTEGER" property="remindCount"/>
<result column="work_order_id" jdbcType="BIGINT" property="workOrderId"/>
<result column="process_definition_id" jdbcType="VARCHAR" property="processDefinitionId"/>
<result column="process_definition_key" jdbcType="VARCHAR" property="processDefinitionKey"/>
<result column="process_definition_name" jdbcType="VARCHAR" property="processDefinitionName"/>
<result column="process_instance_id" jdbcType="VARCHAR" property="processInstanceId"/>
<result column="process_instance_initiator" jdbcType="VARCHAR" property="processInstanceInitiator"/>
<result column="task_id" jdbcType="VARCHAR" property="taskId"/>
<result column="task_definition_key" jdbcType="VARCHAR" property="taskDefinitionKey"/>
<result column="task_name" jdbcType="VARCHAR" property="taskName"/>
<result column="task_start_time" jdbcType="TIMESTAMP" property="taskStartTime"/>
<result column="task_assignee" jdbcType="VARCHAR" property="taskAssignee"/>
<result column="task_finished" jdbcType="BOOLEAN" property="taskFinished"/>
<result column="business_data_shot" jdbcType="LONGVARCHAR" property="businessDataShot"/>
<result column="online_form_data" jdbcType="BOOLEAN" 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"/>
<result column="create_user_id" jdbcType="BIGINT" property="createUserId"/>
<result column="create_username" jdbcType="VARCHAR" property="createUsername"/>
</resultMap>
<sql id="filterRef">
<if test="tenantId == null">
AND a.tenant_id IS NULL
</if>
<if test="tenantId != null">
AND a.tenant_id = #{tenantId}
</if>
<if test="appCode == null">
AND a.app_code IS NULL
</if>
<if test="appCode != null">
AND a.app_code = #{appCode}
</if>
</sql>
<select id="getRemindingMessageListByUser" resultMap="BaseResultMap">
SELECT a.* FROM zz_flow_message a
<where>
<include refid="filterRef"/>
AND a.task_finished = false
AND a.message_type = 0
AND (a.task_assignee = #{loginName} OR EXISTS (SELECT * FROM zz_flow_msg_candidate_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>
<include refid="filterRef"/>
AND a.message_type = 1
AND EXISTS (SELECT * FROM zz_flow_msg_candidate_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_msg_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_msg_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>
<include refid="filterRef"/>
AND a.task_finished = false
AND a.message_type = 0
AND (a.task_assignee = #{loginName} OR EXISTS (SELECT * FROM zz_flow_msg_candidate_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>
<include refid="filterRef"/>
AND a.message_type = 1
AND EXISTS (SELECT * FROM zz_flow_msg_candidate_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_msg_identity_operation c
WHERE a.message_id = c.message_id AND c.login_name = #{loginName})
</where>
</select>
</mapper>

View File

@@ -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.FlowMultiInstanceTransMapper">
<resultMap id="BaseResultMap" type="com.orangeforms.common.flow.model.FlowMultiInstanceTrans">
<id column="id" jdbcType="BIGINT" property="id"/>
<result column="process_instance_id" jdbcType="VARCHAR" property="processInstanceId"/>
<result column="task_id" jdbcType="VARCHAR" property="taskId"/>
<result column="task_key" jdbcType="VARCHAR" property="taskKey"/>
<result column="multi_instance_exec_id" jdbcType="VARCHAR" property="multiInstanceExecId"/>
<result column="execution_id" jdbcType="VARCHAR" property="executionId"/>
<result column="assignee_list" jdbcType="LONGVARCHAR" property="assigneeList"/>
<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>
</mapper>

View File

@@ -0,0 +1,23 @@
<?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.FlowTaskCommentMapper">
<resultMap id="BaseResultMap" type="com.orangeforms.common.flow.model.FlowTaskComment">
<id column="id" jdbcType="BIGINT" property="id"/>
<result column="process_instance_id" jdbcType="VARCHAR" property="processInstanceId"/>
<result column="task_id" jdbcType="VARCHAR" property="taskId"/>
<result column="task_key" jdbcType="VARCHAR" property="taskKey"/>
<result column="task_name" jdbcType="VARCHAR" property="taskName"/>
<result column="target_task_key" jdbcType="VARCHAR" property="targetTaskKey"/>
<result column="execution_id" jdbcType="VARCHAR" property="executionId"/>
<result column="multi_instance_exec_id" jdbcType="VARCHAR" property="multiInstanceExecId"/>
<result column="approval_type" jdbcType="VARCHAR" property="approvalType"/>
<result column="delegate_assignee" jdbcType="VARCHAR" property="delegateAssignee"/>
<result column="custom_business_data" jdbcType="LONGVARCHAR" property="customBusinessData"/>
<result column="task_comment" jdbcType="VARCHAR" property="taskComment"/>
<result column="head_image_url" jdbcType="VARCHAR" property="headImageUrl"/>
<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>
</mapper>

View File

@@ -0,0 +1,36 @@
<?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.FlowTaskExtMapper">
<resultMap id="BaseResultMap" type="com.orangeforms.common.flow.model.FlowTaskExt">
<result column="process_definition_id" jdbcType="VARCHAR" property="processDefinitionId"/>
<result column="task_id" jdbcType="VARCHAR" property="taskId"/>
<result column="operation_list_json" jdbcType="LONGVARCHAR" property="operationListJson"/>
<result column="variable_list_json" jdbcType="LONGVARCHAR" property="variableListJson"/>
<result column="assignee_list_json" jdbcType="LONGVARCHAR" property="assigneeListJson"/>
<result column="group_type" jdbcType="VARCHAR" property="groupType"/>
<result column="dept_post_list_json" jdbcType="VARCHAR" property="deptPostListJson"/>
<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"/>
<result column="extra_data_json" jdbcType="VARCHAR" property="extraDataJson"/>
</resultMap>
<insert id="insertList">
INSERT INTO zz_flow_task_ext VALUES
<foreach collection="list" index="index" item="item" separator="," >
(#{item.processDefinitionId},
#{item.taskId},
#{item.operationListJson},
#{item.variableListJson},
#{item.assigneeListJson},
#{item.groupType},
#{item.deptPostListJson},
#{item.roleIds},
#{item.deptIds},
#{item.candidateUsernames},
#{item.copyListJson},
#{item.extraDataJson})
</foreach>
</insert>
</mapper>

View File

@@ -0,0 +1,15 @@
<?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.FlowWorkOrderExtMapper">
<resultMap id="BaseResultMap" type="com.orangeforms.common.flow.model.FlowWorkOrderExt">
<id column="id" jdbcType="BIGINT" property="id"/>
<result column="work_order_id" jdbcType="BIGINT" property="workOrderId"/>
<result column="draft_data" jdbcType="LONGVARCHAR" property="draftData"/>
<result column="business_data" jdbcType="LONGVARCHAR" property="businessData"/>
<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"/>
<result column="create_user_id" jdbcType="BIGINT" property="createUserId"/>
<result column="deleted_flag" jdbcType="INTEGER" property="deletedFlag"/>
</resultMap>
</mapper>

View File

@@ -0,0 +1,82 @@
<?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.FlowWorkOrderMapper">
<resultMap id="BaseResultMap" type="com.orangeforms.common.flow.model.FlowWorkOrder">
<id column="work_order_id" jdbcType="BIGINT" property="workOrderId"/>
<result column="tenant_id" jdbcType="BIGINT" property="tenantId"/>
<result column="app_code" jdbcType="VARCHAR" property="appCode"/>
<result column="work_order_code" jdbcType="VARCHAR" property="workOrderCode"/>
<result column="process_definition_key" jdbcType="VARCHAR" property="processDefinitionKey"/>
<result column="process_definition_name" jdbcType="VARCHAR" property="processDefinitionName"/>
<result column="process_definition_id" jdbcType="VARCHAR" property="processDefinitionId"/>
<result column="process_instance_id" jdbcType="VARCHAR" property="processInstanceId"/>
<result column="online_table_id" jdbcType="BIGINT" property="onlineTableId"/>
<result column="table_name" jdbcType="VARCHAR" property="tableName"/>
<result column="business_key" jdbcType="VARCHAR" property="businessKey"/>
<result column="latest_approval_status" jdbcType="INTEGER" property="latestApprovalStatus"/>
<result column="flow_status" jdbcType="INTEGER" property="flowStatus"/>
<result column="submit_username" jdbcType="VARCHAR" property="submitUsername"/>
<result column="dept_id" jdbcType="BIGINT" property="deptId"/>
<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"/>
<result column="create_user_id" jdbcType="BIGINT" property="createUserId"/>
<result column="deleted_flag" jdbcType="INTEGER" property="deletedFlag"/>
</resultMap>
<!-- 如果有逻辑删除字段过滤,请写到这里 -->
<sql id="filterRef">
<!-- 这里必须加上全包名否则当filterRef被其他Mapper.xml包含引用的时候就会调用Mapper.xml中的该SQL片段 -->
<include refid="com.orangeforms.common.flow.dao.FlowWorkOrderMapper.inputFilterRef"/>
AND zz_flow_work_order.deleted_flag = ${@com.orangeforms.common.core.constant.GlobalDeletedFlag@NORMAL}
</sql>
<!-- 这里仅包含调用接口输入的主表过滤条件 -->
<sql id="inputFilterRef">
<if test="flowWorkOrderFilter != null">
<if test="flowWorkOrderFilter.tenantId == null">
AND zz_flow_work_order.tenant_id IS NULL
</if>
<if test="flowWorkOrderFilter.tenantId != null">
AND zz_flow_work_order.tenant_id = #{flowWorkOrderFilter.tenantId}
</if>
<if test="flowWorkOrderFilter.appCode == null">
AND zz_flow_work_order.app_code IS NULL
</if>
<if test="flowWorkOrderFilter.appCode != null">
AND zz_flow_work_order.app_code = #{flowWorkOrderFilter.appCode}
</if>
<if test="flowWorkOrderFilter.workOrderCode != null and flowWorkOrderFilter.workOrderCode != ''">
AND zz_flow_work_order.work_order_code = #{flowWorkOrderFilter.workOrderCode}
</if>
<if test="flowWorkOrderFilter.processDefinitionKey != null and flowWorkOrderFilter.processDefinitionKey != ''">
AND zz_flow_work_order.process_definition_key = #{flowWorkOrderFilter.processDefinitionKey}
</if>
<if test="flowWorkOrderFilter.latestApprovalStatus != null">
AND zz_flow_work_order.latest_approval_status = #{flowWorkOrderFilter.latestApprovalStatus}
</if>
<if test="flowWorkOrderFilter.flowStatus != null">
AND zz_flow_work_order.flow_status = #{flowWorkOrderFilter.flowStatus}
</if>
<if test="flowWorkOrderFilter.createTimeStart != null and flowWorkOrderFilter.createTimeStart != ''">
AND zz_flow_work_order.create_time &gt;= #{flowWorkOrderFilter.createTimeStart}
</if>
<if test="flowWorkOrderFilter.createTimeEnd != null and flowWorkOrderFilter.createTimeEnd != ''">
AND zz_flow_work_order.create_time &lt;= #{flowWorkOrderFilter.createTimeEnd}
</if>
<if test="flowWorkOrderFilter.createUserId != null">
AND zz_flow_work_order.create_user_id = #{flowWorkOrderFilter.createUserId}
</if>
</if>
</sql>
<select id="getFlowWorkOrderList" resultMap="BaseResultMap" parameterType="com.orangeforms.common.flow.model.FlowWorkOrder">
SELECT * FROM zz_flow_work_order
<where>
<include refid="filterRef"/>
</where>
<if test="orderBy != null and orderBy != ''">
ORDER BY ${orderBy}
</if>
</select>
</mapper>

View File

@@ -0,0 +1,47 @@
package com.orangeforms.common.flow.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import com.orangeforms.common.core.validator.UpdateGroup;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/**
* 流程分类的Dto对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Schema(description = "流程分类的Dto对象")
@Data
public class FlowCategoryDto {
/**
* 主键Id。
*/
@Schema(description = "主键Id")
@NotNull(message = "数据验证失败主键Id不能为空", groups = {UpdateGroup.class})
private Long categoryId;
/**
* 显示名称。
*/
@Schema(description = "显示名称")
@NotBlank(message = "数据验证失败,显示名称不能为空!")
private String name;
/**
* 分类编码。
*/
@Schema(description = "分类编码")
@NotBlank(message = "数据验证失败,分类编码不能为空!")
private String code;
/**
* 实现顺序。
*/
@Schema(description = "实现顺序")
@NotNull(message = "数据验证失败,实现顺序不能为空!")
private Integer showOrder;
}

View File

@@ -0,0 +1,107 @@
package com.orangeforms.common.flow.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import com.orangeforms.common.core.validator.ConstDictRef;
import com.orangeforms.common.core.validator.UpdateGroup;
import com.orangeforms.common.flow.model.constant.FlowBindFormType;
import com.orangeforms.common.flow.model.constant.FlowEntryStatus;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/**
* 流程的Dto对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Schema(description = "流程的Dto对象")
@Data
public class FlowEntryDto {
/**
* 主键Id。
*/
@Schema(description = "主键Id")
@NotNull(message = "数据验证失败,主键不能为空!", groups = {UpdateGroup.class})
private Long entryId;
/**
* 流程名称。
*/
@Schema(description = "流程名称")
@NotBlank(message = "数据验证失败,流程名称不能为空!")
private String processDefinitionName;
/**
* 流程标识Key。
*/
@Schema(description = "流程标识Key")
@NotBlank(message = "数据验证失败流程标识Key不能为空")
private String processDefinitionKey;
/**
* 流程分类。
*/
@Schema(description = "流程分类")
@NotNull(message = "数据验证失败,流程分类不能为空!")
private Long categoryId;
/**
* 流程状态。
*/
@Schema(description = "流程状态")
@ConstDictRef(constDictClass = FlowEntryStatus.class, message = "数据验证失败,工作流状态为无效值!")
private Integer status;
/**
* 流程定义的xml。
*/
@Schema(description = "流程定义的xml")
private String bpmnXml;
/**
* 流程图类型。0: 普通流程图1: 钉钉风格的流程图。
*/
@Schema(description = "流程图类型。0: 普通流程图1: 钉钉风格的流程图")
private Integer diagramType;
/**
* 绑定表单类型。
*/
@Schema(description = "绑定表单类型")
@ConstDictRef(constDictClass = FlowBindFormType.class, message = "数据验证失败,工作流绑定表单类型为无效值!")
@NotNull(message = "数据验证失败,工作流绑定表单类型不能为空!")
private Integer bindFormType;
/**
* 在线表单的页面Id。
*/
@Schema(description = "在线表单的页面Id")
private Long pageId;
/**
* 在线表单的缺省路由名称。
*/
@Schema(description = "在线表单的缺省路由名称")
private String defaultRouterName;
/**
* 在线表单Id。
*/
@Schema(description = "在线表单Id")
private Long defaultFormId;
/**
* 工单表编码字段的编码规则,如果为空则不计算工单编码。
*/
@Schema(description = "工单表编码字段的编码规则")
private String encodedRule;
/**
* 流程的自定义扩展数据(JSON格式)。
*/
@Schema(description = "流程的自定义扩展数据")
private String extensionData;
}

View File

@@ -0,0 +1,81 @@
package com.orangeforms.common.flow.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import com.orangeforms.common.core.validator.ConstDictRef;
import com.orangeforms.common.core.validator.UpdateGroup;
import com.orangeforms.common.flow.model.constant.FlowVariableType;
import lombok.Data;
import jakarta.validation.constraints.*;
/**
* 流程变量Dto对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Schema(description = "流程变量Dto对象")
@Data
public class FlowEntryVariableDto {
/**
* 主键Id。
*/
@Schema(description = "主键Id")
@NotNull(message = "数据验证失败主键Id不能为空", groups = {UpdateGroup.class})
private Long variableId;
/**
* 流程Id。
*/
@Schema(description = "流程Id")
@NotNull(message = "数据验证失败流程Id不能为空")
private Long entryId;
/**
* 变量名。
*/
@Schema(description = "变量名")
@NotBlank(message = "数据验证失败,变量名不能为空!")
private String variableName;
/**
* 显示名。
*/
@Schema(description = "显示名")
@NotBlank(message = "数据验证失败,显示名不能为空!")
private String showName;
/**
* 流程变量类型。
*/
@Schema(description = "流程变量类型")
@ConstDictRef(constDictClass = FlowVariableType.class, message = "数据验证失败,流程变量类型为无效值!")
@NotNull(message = "数据验证失败,流程变量类型不能为空!")
private Integer variableType;
/**
* 绑定数据源Id。
*/
@Schema(description = "绑定数据源Id")
private Long bindDatasourceId;
/**
* 绑定数据源关联Id。
*/
@Schema(description = "绑定数据源关联Id")
private Long bindRelationId;
/**
* 绑定字段Id。
*/
@Schema(description = "绑定字段Id")
private Long bindColumnId;
/**
* 是否内置。
*/
@Schema(description = "是否内置")
@NotNull(message = "数据验证失败,是否内置不能为空!")
private Boolean builtin;
}

View File

@@ -0,0 +1,51 @@
package com.orangeforms.common.flow.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 工作流通知消息Dto对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Schema(description = "工作流通知消息Dto对象")
@Data
public class FlowMessageDto {
/**
* 消息类型。
*/
@Schema(description = "消息类型")
private Integer messageType;
/**
* 工单Id。
*/
@Schema(description = "工单Id")
private Long workOrderId;
/**
* 流程名称。
*/
@Schema(description = "流程名称")
private String processDefinitionName;
/**
* 流程任务名称。
*/
@Schema(description = "流程任务名称")
private String taskName;
/**
* 更新时间范围过滤起始值(>=)。
*/
@Schema(description = "updateTime 范围过滤起始值")
private String updateTimeStart;
/**
* 更新时间范围过滤结束值(<=)。
*/
@Schema(description = "updateTime 范围过滤结束值")
private String updateTimeEnd;
}

View File

@@ -0,0 +1,38 @@
package com.orangeforms.common.flow.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/**
* 流程任务的批注。
*
* @author Jerry
* @date 2024-07-02
*/
@Schema(description = "流程任务的批注")
@Data
public class FlowTaskCommentDto {
/**
* 流程任务触发按钮类型内置值可参考FlowTaskButton。
*/
@Schema(description = "流程任务触发按钮类型")
@NotNull(message = "数据验证失败,任务的审批类型不能为空!")
private String approvalType;
/**
* 流程任务的批注内容。
*/
@Schema(description = "流程任务的批注内容")
@NotBlank(message = "数据验证失败,任务审批内容不能为空!")
private String taskComment;
/**
* 委托指定人,比如加签、转办等。
*/
@Schema(description = "委托指定人,比如加签、转办等")
private String delegateAssignee;
}

View File

@@ -0,0 +1,39 @@
package com.orangeforms.common.flow.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 工作流工单Dto对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Schema(description = "工作流工单Dto对象")
@Data
public class FlowWorkOrderDto {
/**
* 工单编码。
*/
@Schema(description = "工单编码")
private String workOrderCode;
/**
* 流程状态。参考FlowTaskStatus常量值对象。
*/
@Schema(description = "流程状态")
private Integer flowStatus;
/**
* createTime 范围过滤起始值(>=)。
*/
@Schema(description = "createTime 范围过滤起始值")
private String createTimeStart;
/**
* createTime 范围过滤结束值(<=)。
*/
@Schema(description = "createTime 范围过滤结束值")
private String createTimeEnd;
}

View File

@@ -0,0 +1,21 @@
package com.orangeforms.common.flow.exception;
import org.flowable.common.engine.api.FlowableException;
/**
* 流程空用户异常。
*
* @author Jerry
* @date 2024-07-02
*/
public class FlowEmptyUserException extends FlowableException {
/**
* 构造函数。
*
* @param msg 错误信息。
*/
public FlowEmptyUserException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,35 @@
package com.orangeforms.common.flow.exception;
/**
* 流程操作异常。
*
* @author Jerry
* @date 2024-07-02
*/
public class FlowOperationException extends RuntimeException {
/**
* 构造函数。
*/
public FlowOperationException() {
}
/**
* 构造函数。
*
* @param throwable 引发异常对象。
*/
public FlowOperationException(Throwable throwable) {
super(throwable);
}
/**
* 构造函数。
*
* @param msg 错误信息。
*/
public FlowOperationException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,165 @@
package com.orangeforms.common.flow.listener;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.orangeforms.common.core.object.TokenData;
import com.orangeforms.common.core.util.ApplicationContextHolder;
import com.orangeforms.common.flow.constant.FlowApprovalType;
import com.orangeforms.common.flow.constant.FlowConstant;
import com.orangeforms.common.flow.model.FlowTaskComment;
import com.orangeforms.common.flow.model.FlowTaskExt;
import com.orangeforms.common.flow.object.FlowTaskOperation;
import com.orangeforms.common.flow.service.FlowApiService;
import com.orangeforms.common.flow.service.FlowTaskCommentService;
import com.orangeforms.common.flow.service.FlowTaskExtService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.ExtensionAttribute;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.api.Task;
import org.flowable.task.service.delegate.DelegateTask;
import java.util.*;
/**
* 流程任务自动审批跳过的监听器。
*
* @author Jerry
* @date 2024-07-02
*/
@Slf4j
public class AutoSkipTaskListener implements TaskListener {
private final transient FlowTaskCommentService flowTaskCommentService =
ApplicationContextHolder.getBean(FlowTaskCommentService.class);
private final transient FlowApiService flowApiService =
ApplicationContextHolder.getBean(FlowApiService.class);
private final transient FlowTaskExtService flowTaskExtService =
ApplicationContextHolder.getBean(FlowTaskExtService.class);
/**
* 流程的发起者等于当前任务的Assignee。
*/
private static final String EQ_START_USER = "0";
/**
* 上一步的提交者等于当前任务的Assignee。
*/
private static final String EQ_PREV_SUBMIT_USER = "1";
/**
* 当前任务的Assignee之前提交过审核。
*/
private static final String EQ_HISTORIC_SUBMIT_USER = "2";
@Override
public void notify(DelegateTask t) {
UserTask userTask = flowApiService.getUserTask(t.getProcessDefinitionId(), t.getTaskDefinitionKey());
List<ExtensionAttribute> attributes = userTask.getAttributes().get(FlowConstant.USER_TASK_AUTO_SKIP_KEY);
Set<String> skipTypes = new HashSet<>(StrUtil.split(attributes.get(0).getValue(), ","));
String assignedUser = this.getAssignedUser(userTask, t.getProcessDefinitionId(), t.getExecutionId());
if (StrUtil.isBlank(assignedUser)) {
return;
}
for (String skipType : skipTypes) {
if (this.verifyAndHandle(userTask, t, skipType, assignedUser)) {
return;
}
}
}
private boolean verifyAndHandle(UserTask userTask, DelegateTask task, String skipType, String assignedUser) {
FlowTaskComment comment = null;
switch (skipType) {
case EQ_START_USER:
Object v = task.getVariable(FlowConstant.PROC_INSTANCE_START_USER_NAME_VAR);
if (ObjectUtil.equal(v, assignedUser)) {
comment = flowTaskCommentService.getFirstFlowTaskComment(task.getProcessInstanceId());
}
break;
case EQ_PREV_SUBMIT_USER:
Object v2 = task.getVariable(FlowConstant.SUBMIT_USER_VAR);
if (ObjectUtil.equal(v2, assignedUser)) {
TokenData tokenData = TokenData.takeFromRequest();
comment = new FlowTaskComment();
comment.setCreateUserId(tokenData.getUserId());
comment.setCreateLoginName(tokenData.getLoginName());
comment.setCreateUsername(tokenData.getShowName());
}
break;
case EQ_HISTORIC_SUBMIT_USER:
List<FlowTaskComment> comments =
flowTaskCommentService.getFlowTaskCommentList(task.getProcessInstanceId());
List<FlowTaskComment> resultComments = new LinkedList<>();
for (FlowTaskComment c : comments) {
if (StrUtil.equals(c.getCreateLoginName(), assignedUser)) {
resultComments.add(c);
}
}
if (CollUtil.isNotEmpty(resultComments)) {
comment = resultComments.get(0);
}
break;
default:
break;
}
if (comment != null) {
FlowTaskExt flowTaskExt = flowTaskExtService
.getByProcessDefinitionIdAndTaskId(task.getProcessDefinitionId(), userTask.getId());
JSONObject taskVariableData = new JSONObject();
if (StrUtil.isNotBlank(flowTaskExt.getOperationListJson())) {
List<FlowTaskOperation> taskOperationList =
JSONArray.parseArray(flowTaskExt.getOperationListJson(), FlowTaskOperation.class);
taskOperationList.stream()
.filter(op -> op.getType().equals(FlowApprovalType.AGREE))
.map(FlowTaskOperation::getLatestApprovalStatus).findFirst()
.ifPresent(status -> taskVariableData.put(FlowConstant.LATEST_APPROVAL_STATUS_KEY, status));
}
Task t = flowApiService.getTaskById(task.getId());
comment.fillWith(t);
comment.setApprovalType(FlowApprovalType.AGREE);
comment.setTaskComment(StrFormatter.format("自动跳过审批。审批人 [{}], 跳过原因 [{}]。",
userTask.getAssignee(), this.getMessageBySkipType(skipType)));
flowApiService.completeTask(t, comment, taskVariableData);
}
return comment != null;
}
private String getAssignedUser(UserTask userTask, String processDefinitionId, String executionId) {
String assignedUser = userTask.getAssignee();
if (StrUtil.isNotBlank(assignedUser)) {
if (assignedUser.startsWith("${") && assignedUser.endsWith("}")) {
String variableName = assignedUser.substring(2, assignedUser.length() - 1);
assignedUser = flowApiService.getExecutionVariableStringWithSafe(executionId, variableName);
}
} else {
FlowTaskExt flowTaskExt = flowTaskExtService
.getByProcessDefinitionIdAndTaskId(processDefinitionId, userTask.getId());
List<String> candidateUsernames;
if (StrUtil.isBlank(flowTaskExt.getCandidateUsernames())) {
candidateUsernames = Collections.emptyList();
} else if (!StrUtil.equals(flowTaskExt.getCandidateUsernames(), "${" + FlowConstant.TASK_APPOINTED_ASSIGNEE_VAR + "}")) {
candidateUsernames = StrUtil.split(flowTaskExt.getCandidateUsernames(), ",");
} else {
String value = flowApiService
.getExecutionVariableStringWithSafe(executionId, FlowConstant.TASK_APPOINTED_ASSIGNEE_VAR);
candidateUsernames = value == null ? null : StrUtil.split(value, ",");
}
if (candidateUsernames != null && candidateUsernames.size() == 1) {
assignedUser = candidateUsernames.get(0);
}
}
return assignedUser;
}
private String getMessageBySkipType(String skipType) {
return switch (skipType) {
case EQ_PREV_SUBMIT_USER -> "审批人与上一审批节点处理人相同";
case EQ_START_USER -> "审批人为发起人";
case EQ_HISTORIC_SUBMIT_USER -> "审批人审批过";
default -> "";
};
}
}

View File

@@ -0,0 +1,27 @@
package com.orangeforms.common.flow.listener;
import com.orangeforms.common.flow.constant.FlowConstant;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask;
import java.util.Map;
/**
* 当用户任务的候选组为本部门领导岗位时,该监听器会在任务创建时,获取当前流程实例发起人的部门领导。
* 并将其指派为当前任务的候选组。
*
* @author Jerry
* @date 2024-07-02
*/
@Slf4j
public class DeptPostLeaderListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
Map<String, Object> variables = delegateTask.getVariables();
if (variables.get(FlowConstant.GROUP_TYPE_DEPT_POST_LEADER_VAR) == null) {
delegateTask.setAssignee(variables.get(FlowConstant.PROC_INSTANCE_START_USER_NAME_VAR).toString());
}
}
}

View File

@@ -0,0 +1,56 @@
package com.orangeforms.common.flow.listener;
import cn.hutool.core.util.StrUtil;
import com.orangeforms.common.core.object.GlobalThreadLocal;
import com.orangeforms.common.core.util.ApplicationContextHolder;
import com.orangeforms.common.flow.constant.FlowTaskStatus;
import com.orangeforms.common.flow.model.FlowWorkOrder;
import com.orangeforms.common.flow.service.FlowWorkOrderService;
import com.orangeforms.common.flow.util.FlowCustomExtFactory;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
/**
* 流程实例监听器,在流程实例结束的时候,需要完成一些自定义的业务行为。如:
* 1. 更新流程工单表的审批状态字段。
* 2. 业务数据同步。
*
* @author Jerry
* @date 2024-07-02
*/
@Slf4j
public class FlowFinishedListener implements ExecutionListener {
private final transient FlowWorkOrderService flowWorkOrderService =
ApplicationContextHolder.getBean(FlowWorkOrderService.class);
private final transient FlowCustomExtFactory flowCustomExtFactory =
ApplicationContextHolder.getBean(FlowCustomExtFactory.class);
@Override
public void notify(DelegateExecution execution) {
if (!StrUtil.equals("end", execution.getEventName())) {
return;
}
boolean enabled = GlobalThreadLocal.setDataFilter(false);
try {
String processInstanceId = execution.getProcessInstanceId();
FlowWorkOrder workOrder = flowWorkOrderService.getFlowWorkOrderByProcessInstanceId(processInstanceId);
if (workOrder == null) {
return;
}
int flowStatus = FlowTaskStatus.FINISHED;
if (workOrder.getFlowStatus().equals(FlowTaskStatus.CANCELLED)
|| workOrder.getFlowStatus().equals(FlowTaskStatus.STOPPED)) {
flowStatus = workOrder.getFlowStatus();
}
workOrder.setFlowStatus(flowStatus);
// 更新流程工单中的流程状态。
flowWorkOrderService.updateFlowStatusByProcessInstanceId(processInstanceId, flowStatus);
// 处理在线表单工作流的自定义状态更新。
flowCustomExtFactory.getOnlineBusinessDataExtHelper().updateFlowStatus(workOrder);
} finally {
GlobalThreadLocal.setDataFilter(enabled);
}
}
}

View File

@@ -0,0 +1,80 @@
package com.orangeforms.common.flow.listener;
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.orangeforms.common.core.util.ApplicationContextHolder;
import com.orangeforms.common.flow.constant.FlowConstant;
import com.orangeforms.common.flow.model.FlowTaskExt;
import com.orangeforms.common.flow.object.FlowUserTaskExtData;
import com.orangeforms.common.flow.service.FlowApiService;
import com.orangeforms.common.flow.service.FlowTaskExtService;
import com.orangeforms.common.flow.util.BaseFlowNotifyExtHelper;
import com.orangeforms.common.flow.util.FlowCustomExtFactory;
import com.orangeforms.common.flow.vo.FlowTaskVo;
import com.orangeforms.common.flow.vo.FlowUserInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.service.delegate.DelegateTask;
import java.util.List;
/**
* 任务进入待办状态时的通知监听器。
*
* @author Jerry
* @date 2024-07-02
*/
@Slf4j
public class FlowTaskNotifyListener implements TaskListener {
private final transient FlowTaskExtService flowTaskExtService =
ApplicationContextHolder.getBean(FlowTaskExtService.class);
private final transient FlowApiService flowApiService =
ApplicationContextHolder.getBean(FlowApiService.class);
private final transient FlowCustomExtFactory flowCustomExtFactory =
ApplicationContextHolder.getBean(FlowCustomExtFactory.class);
@Override
public void notify(DelegateTask delegateTask) {
String definitionId = delegateTask.getProcessDefinitionId();
String instanceId = delegateTask.getProcessInstanceId();
String taskId = delegateTask.getId();
String taskKey = delegateTask.getTaskDefinitionKey();
FlowTaskExt taskExt = flowTaskExtService.getByProcessDefinitionIdAndTaskId(definitionId, taskKey);
if (StrUtil.isBlank(taskExt.getExtraDataJson())) {
return;
}
FlowUserTaskExtData extData = JSON.parseObject(taskExt.getExtraDataJson(), FlowUserTaskExtData.class);
if (CollUtil.isEmpty(extData.getFlowNotifyTypeList())) {
return;
}
ProcessInstance instance = flowApiService.getProcessInstance(instanceId);
Object initiator = flowApiService.getProcessInstanceVariable(instanceId, FlowConstant.PROC_INSTANCE_INITIATOR_VAR);
boolean isMultiInstanceTask = flowApiService.isMultiInstanceTask(definitionId, taskKey);
Task task = flowApiService.getProcessInstanceActiveTask(instanceId, taskId);
List<FlowUserInfoVo> userInfoList =
flowTaskExtService.getCandidateUserInfoList(instanceId, taskExt, task, isMultiInstanceTask, false);
if (CollUtil.isEmpty(userInfoList)) {
log.warn("ProcessDefinition [{}] Task [{}] don't find the candidate users for notification.",
instance.getProcessDefinitionName(), task.getName());
return;
}
BaseFlowNotifyExtHelper helper = flowCustomExtFactory.getFlowNotifyExtHelper();
Assert.notNull(helper);
for (String notifyType : extData.getFlowNotifyTypeList()) {
FlowTaskVo flowTaskVo = new FlowTaskVo();
flowTaskVo.setProcessDefinitionId(definitionId);
flowTaskVo.setProcessInstanceId(instanceId);
flowTaskVo.setTaskKey(taskKey);
flowTaskVo.setTaskName(delegateTask.getName());
flowTaskVo.setTaskId(delegateTask.getId());
flowTaskVo.setBusinessKey(instance.getBusinessKey());
flowTaskVo.setProcessInstanceInitiator(initiator.toString());
helper.doNotify(notifyType, userInfoList, flowTaskVo);
}
}
}

View File

@@ -0,0 +1,32 @@
package com.orangeforms.common.flow.listener;
import com.orangeforms.common.core.util.ApplicationContextHolder;
import com.orangeforms.common.flow.constant.FlowConstant;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask;
import java.util.Map;
/**
* 流程任务通用监听器。
*
* @author Jerry
* @date 2024-07-02
*/
@Slf4j
public class FlowUserTaskListener implements TaskListener {
private final transient RuntimeService runtimeService =
ApplicationContextHolder.getBean(RuntimeService.class);
@Override
public void notify(DelegateTask delegateTask) {
Map<String, Object> variables = delegateTask.getVariables();
if (variables.get(FlowConstant.DELEGATE_ASSIGNEE_VAR) != null) {
delegateTask.setAssignee(variables.get(FlowConstant.DELEGATE_ASSIGNEE_VAR).toString());
runtimeService.removeVariableLocal(delegateTask.getExecutionId(), FlowConstant.DELEGATE_ASSIGNEE_VAR);
}
}
}

View File

@@ -0,0 +1,27 @@
package com.orangeforms.common.flow.listener;
import com.orangeforms.common.flow.constant.FlowConstant;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask;
import java.util.Map;
/**
* 当用户任务的候选组为上级部门领导岗位时,该监听器会在任务创建时,获取当前流程实例发起人的部门领导。
* 并将其指派为当前任务的候选组。
*
* @author Jerry
* @date 2024-07-02
*/
@Slf4j
public class UpDeptPostLeaderListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
Map<String, Object> variables = delegateTask.getVariables();
if (variables.get(FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER_VAR) == null) {
delegateTask.setAssignee(variables.get(FlowConstant.PROC_INSTANCE_START_USER_NAME_VAR).toString());
}
}
}

View File

@@ -0,0 +1,44 @@
package com.orangeforms.common.flow.listener;
import cn.hutool.core.util.StrUtil;
import com.orangeforms.common.core.util.ApplicationContextHolder;
import com.orangeforms.common.flow.model.FlowWorkOrder;
import com.orangeforms.common.flow.service.FlowWorkOrderService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
import org.flowable.engine.impl.el.FixedValue;
/**
* 更新流程的最后审批状态的监听器,目前用于排他网关到任务结束节点的连线上,
* 以便于准确的判断流程实例的最后审批状态。
*
* @author Jerry
* @date 2024-07-02
*/
@Slf4j
public class UpdateLatestApprovalStatusListener implements ExecutionListener {
private FixedValue latestApprovalStatus;
private final transient FlowWorkOrderService flowWorkOrderService =
ApplicationContextHolder.getBean(FlowWorkOrderService.class);
public void setAutoStoreVariablesExp(FixedValue approvalStatus) {
this.latestApprovalStatus = approvalStatus;
}
@Override
public void notify(DelegateExecution execution) {
if (StrUtil.isNotBlank(latestApprovalStatus.getExpressionText())) {
FlowWorkOrder workOrder =
flowWorkOrderService.getFlowWorkOrderByProcessInstanceId(execution.getProcessInstanceId());
if (workOrder == null) {
return;
}
Integer approvalStatus = Integer.valueOf(latestApprovalStatus.getExpressionText());
String processInstanceId = execution.getProcessInstanceId();
flowWorkOrderService.updateLatestApprovalStatusByProcessInstanceId(processInstanceId, approvalStatus);
}
}
}

View File

@@ -0,0 +1,77 @@
package com.orangeforms.common.flow.model;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;
/**
* 流程分类的实体对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
@TableName(value = "zz_flow_category")
public class FlowCategory {
/**
* 主键Id。
*/
@TableId(value = "category_id")
private Long categoryId;
/**
* 租户Id。
*/
@TableField(value = "tenant_id")
private Long tenantId;
/**
* 应用编码。为空时,表示非第三方应用接入。
*/
@TableField(value = "app_code")
private String appCode;
/**
* 显示名称。
*/
@TableField(value = "name")
private String name;
/**
* 分类编码。
*/
@TableField(value = "code")
private String code;
/**
* 实现顺序。
*/
@TableField(value = "show_order")
private Integer showOrder;
/**
* 更新时间。
*/
@TableField(value = "update_time")
private Date updateTime;
/**
* 更新者Id。
*/
@TableField(value = "update_user_id")
private Long updateUserId;
/**
* 创建时间。
*/
@TableField(value = "create_time")
private Date createTime;
/**
* 创建者Id。
*/
@TableField(value = "create_user_id")
private Long createUserId;
}

View File

@@ -0,0 +1,154 @@
package com.orangeforms.common.flow.model;
import com.baomidou.mybatisplus.annotation.*;
import com.orangeforms.common.core.annotation.RelationOneToOne;
import lombok.Data;
import java.util.Date;
/**
* 流程的实体对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
@TableName(value = "zz_flow_entry")
public class FlowEntry {
/**
* 主键。
*/
@TableId(value = "entry_id")
private Long entryId;
/**
* 租户Id。
*/
@TableField(value = "tenant_id")
private Long tenantId;
/**
* 应用编码。为空时,表示非第三方应用接入。
*/
@TableField(value = "app_code")
private String appCode;
/**
* 流程名称。
*/
@TableField(value = "process_definition_name")
private String processDefinitionName;
/**
* 流程标识Key。
*/
@TableField(value = "process_definition_key")
private String processDefinitionKey;
/**
* 流程分类。
*/
@TableField(value = "category_id")
private Long categoryId;
/**
* 工作流部署的发布主版本Id。
*/
@TableField(value = "main_entry_publish_id")
private Long mainEntryPublishId;
/**
* 最新发布时间。
*/
@TableField(value = "latest_publish_time")
private Date latestPublishTime;
/**
* 流程状态。
*/
@TableField(value = "status")
private Integer status;
/**
* 流程定义的xml。
*/
@TableField(value = "bpmn_xml")
private String bpmnXml;
/**
* 流程图类型。0: 普通流程图1: 钉钉风格的流程图。
*/
@TableField(value = "diagram_type")
private Integer diagramType;
/**
* 绑定表单类型。
*/
@TableField(value = "bind_form_type")
private Integer bindFormType;
/**
* 在线表单的页面Id。
*/
@TableField(value = "page_id")
private Long pageId;
/**
* 在线表单Id。
*/
@TableField(value = "default_form_id")
private Long defaultFormId;
/**
* 静态表单的缺省路由名称。
*/
@TableField(value = "default_router_name")
private String defaultRouterName;
/**
* 工单表编码字段的编码规则,如果为空则不计算工单编码。
*/
@TableField(value = "encoded_rule")
private String encodedRule;
/**
* 流程的自定义扩展数据(JSON格式)。
*/
@TableField(value = "extension_data")
private String extensionData;
/**
* 更新时间。
*/
@TableField(value = "update_time")
private Date updateTime;
/**
* 更新者Id。
*/
@TableField(value = "update_user_id")
private Long updateUserId;
/**
* 创建时间。
*/
@TableField(value = "create_time")
private Date createTime;
/**
* 创建者Id。
*/
@TableField(value = "create_user_id")
private Long createUserId;
@TableField(exist = false)
private FlowEntryPublish mainFlowEntryPublish;
@RelationOneToOne(
masterIdField = "categoryId",
slaveModelClass = FlowCategory.class,
slaveIdField = "categoryId")
@TableField(exist = false)
private FlowCategory flowCategory;
}

View File

@@ -0,0 +1,89 @@
package com.orangeforms.common.flow.model;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;
/**
* 流程发布数据的实体对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
@TableName(value = "zz_flow_entry_publish")
public class FlowEntryPublish {
/**
* 主键Id。
*/
@TableId(value = "entry_publish_id")
private Long entryPublishId;
/**
* 流程Id。
*/
@TableField(value = "entry_id")
private Long entryId;
/**
* 流程引擎的部署Id。
*/
@TableField(value = "deploy_id")
private String deployId;
/**
* 流程引擎中的流程定义Id。
*/
@TableField(value = "process_definition_id")
private String processDefinitionId;
/**
* 发布版本。
*/
@TableField(value = "publish_version")
private Integer publishVersion;
/**
* 激活状态。
*/
@TableField(value = "active_status")
private Boolean activeStatus;
/**
* 是否为主版本。
*/
@TableField(value = "main_version")
private Boolean mainVersion;
/**
* 创建者Id。
*/
@TableField(value = "create_user_id")
private Long createUserId;
/**
* 发布时间。
*/
@TableField(value = "publish_time")
private Date publishTime;
/**
* 第一个非开始节点任务的附加信息。
*/
@TableField(value = "init_task_info")
private String initTaskInfo;
/**
* 分析后的节点JSON信息。
*/
@TableField(value = "analyzed_node_json")
private String analyzedNodeJson;
/**
* 流程的自定义扩展数据(JSON格式)。
*/
@TableField(value = "extension_data")
private String extensionData;
}

View File

@@ -0,0 +1,69 @@
package com.orangeforms.common.flow.model;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
/**
* FlowEntryPublishVariable实体对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
@TableName(value = "zz_flow_entry_publish_variable")
public class FlowEntryPublishVariable {
/**
* 主键Id。
*/
@TableId(value = "variable_id")
private Long variableId;
/**
* 流程Id。
*/
@TableField(value = "entry_publish_id")
private Long entryPublishId;
/**
* 变量名。
*/
@TableField(value = "variable_name")
private String variableName;
/**
* 显示名。
*/
@TableField(value = "show_name")
private String showName;
/**
* 变量类型。
*/
@TableField(value = "variable_type")
private Integer variableType;
/**
* 是否内置。
*/
@TableField(value = "builtin")
private Boolean builtin;
/**
* 绑定数据源Id。
*/
@TableField(value = "bind_datasource_id")
private Long bindDatasourceId;
/**
* 绑定数据源关联Id。
*/
@TableField(value = "bind_relation_id")
private Long bindRelationId;
/**
* 绑定字段Id。
*/
@TableField(value = "bind_column_id")
private Long bindColumnId;
}

View File

@@ -0,0 +1,77 @@
package com.orangeforms.common.flow.model;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;
/**
* 流程变量实体对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
@TableName(value = "zz_flow_entry_variable")
public class FlowEntryVariable {
/**
* 主键Id。
*/
@TableId(value = "variable_id")
private Long variableId;
/**
* 流程Id。
*/
@TableField(value = "entry_id")
private Long entryId;
/**
* 变量名。
*/
@TableField(value = "variable_name")
private String variableName;
/**
* 显示名。
*/
@TableField(value = "show_name")
private String showName;
/**
* 流程变量类型。
*/
@TableField(value = "variable_type")
private Integer variableType;
/**
* 绑定数据源Id。
*/
@TableField(value = "bind_datasource_id")
private Long bindDatasourceId;
/**
* 绑定数据源关联Id。
*/
@TableField(value = "bind_relation_id")
private Long bindRelationId;
/**
* 绑定字段Id。
*/
@TableField(value = "bind_column_id")
private Long bindColumnId;
/**
* 是否内置。
*/
@TableField(value = "builtin")
private Boolean builtin;
/**
* 创建时间。
*/
@TableField(value = "create_time")
private Date createTime;
}

View File

@@ -0,0 +1,167 @@
package com.orangeforms.common.flow.model;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;
/**
* 工作流通知消息实体对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
@TableName(value = "zz_flow_message")
public class FlowMessage {
/**
* 主键Id。
*/
@TableId(value = "message_id")
private Long messageId;
/**
* 租户Id。
*/
@TableField(value = "tenant_id")
private Long tenantId;
/**
* 应用编码。为空时,表示非第三方应用接入。
*/
@TableField(value = "app_code")
private String appCode;
/**
* 消息类型。
*/
@TableField(value = "message_type")
private Integer messageType;
/**
* 消息内容。
*/
@TableField(value = "message_content")
private String messageContent;
/**
* 催办次数。
*/
@TableField(value = "remind_count")
private Integer remindCount;
/**
* 工单Id。
*/
@TableField(value = "work_order_id")
private Long workOrderId;
/**
* 流程定义Id。
*/
@TableField(value = "process_definition_id")
private String processDefinitionId;
/**
* 流程定义标识。
*/
@TableField(value = "process_definition_key")
private String processDefinitionKey;
/**
* 流程名称。
*/
@TableField(value = "process_definition_name")
private String processDefinitionName;
/**
* 流程实例Id。
*/
@TableField(value = "process_instance_id")
private String processInstanceId;
/**
* 流程实例发起者。
*/
@TableField(value = "process_instance_initiator")
private String processInstanceInitiator;
/**
* 流程任务Id。
*/
@TableField(value = "task_id")
private String taskId;
/**
* 流程任务定义标识。
*/
@TableField(value = "task_definition_key")
private String taskDefinitionKey;
/**
* 流程任务名称。
*/
@TableField(value = "task_name")
private String taskName;
/**
* 创建时间。
*/
@TableField(value = "task_start_time")
private Date taskStartTime;
/**
* 任务指派人登录名。
*/
@TableField(value = "task_assignee")
private String taskAssignee;
/**
* 任务是否已完成。
*/
@TableField(value = "task_finished")
private Boolean taskFinished;
/**
* 业务数据快照。
*/
@TableField(value = "business_data_shot")
private String businessDataShot;
/**
* 是否为在线表单消息数据。
*/
@TableField(value = "online_form_data")
private Boolean onlineFormData;
/**
* 更新时间。
*/
@TableField(value = "update_time")
private Date updateTime;
/**
* 更新者Id。
*/
@TableField(value = "update_user_id")
private Long updateUserId;
/**
* 创建时间。
*/
@TableField(value = "create_time")
private Date createTime;
/**
* 创建者Id。
*/
@TableField(value = "create_user_id")
private Long createUserId;
/**
* 创建者显示名。
*/
@TableField(value = "create_username")
private String createUsername;
}

View File

@@ -0,0 +1,39 @@
package com.orangeforms.common.flow.model;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
/**
* 流程任务消息的候选身份实体对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
@TableName(value = "zz_flow_msg_candidate_identity")
public class FlowMessageCandidateIdentity {
/**
* 主键Id。
*/
@TableId(value = "id")
private Long id;
/**
* 任务消息Id。
*/
@TableField(value = "message_id")
private Long messageId;
/**
* 候选身份类型。
*/
@TableField(value = "candidate_type")
private String candidateType;
/**
* 候选身份Id。
*/
@TableField(value = "candidate_id")
private String candidateId;
}

View File

@@ -0,0 +1,48 @@
package com.orangeforms.common.flow.model;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;
/**
* 流程任务消息所属用户的操作表。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
@TableName(value = "zz_flow_msg_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;
}

View File

@@ -0,0 +1,97 @@
package com.orangeforms.common.flow.model;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.flowable.task.api.TaskInfo;
import java.util.Date;
/**
* 流程多实例任务执行流水对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
@NoArgsConstructor
@TableName(value = "zz_flow_multi_instance_trans")
public class FlowMultiInstanceTrans {
/**
* 主键Id。
*/
@TableId(value = "id")
private Long id;
/**
* 流程实例Id。
*/
@TableField(value = "process_instance_id")
private String processInstanceId;
/**
* 任务Id。
*/
@TableField(value = "task_id")
private String taskId;
/**
* 任务标识。
*/
@TableField(value = "task_key")
private String taskKey;
/**
* 会签任务的执行Id。
*/
@TableField(value = "multi_instance_exec_id")
private String multiInstanceExecId;
/**
* 任务的执行Id。
*/
@TableField(value = "execution_id")
private String executionId;
/**
* 会签指派人列表。
*/
@TableField(value = "assignee_list")
private String assigneeList;
/**
* 创建者Id。
*/
@TableField(value = "create_user_id")
private Long createUserId;
/**
* 创建者登录名。
*/
@TableField(value = "create_login_name")
private String createLoginName;
/**
* 创建者显示名。
*/
@TableField(value = "create_username")
private String createUsername;
/**
* 创建时间。
*/
@TableField(value = "create_time")
private Date createTime;
public FlowMultiInstanceTrans(TaskInfo task) {
this.fillWith(task);
}
public void fillWith(TaskInfo task) {
this.taskId = task.getId();
this.taskKey = task.getTaskDefinitionKey();
this.processInstanceId = task.getProcessInstanceId();
this.executionId = task.getExecutionId();
}
}

View File

@@ -0,0 +1,150 @@
package com.orangeforms.common.flow.model;
import com.baomidou.mybatisplus.annotation.*;
import com.orangeforms.common.core.util.ContextUtil;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.flowable.task.api.TaskInfo;
import java.util.Date;
/**
* FlowTaskComment实体对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
@NoArgsConstructor
@TableName(value = "zz_flow_task_comment")
public class FlowTaskComment {
/**
* 主键Id。
*/
@TableId(value = "id")
private Long id;
/**
* 流程实例Id。
*/
@TableField(value = "process_instance_id")
private String processInstanceId;
/**
* 任务Id。
*/
@TableField(value = "task_id")
private String taskId;
/**
* 任务标识。
*/
@TableField(value = "task_key")
private String taskKey;
/**
* 任务名称。
*/
@TableField(value = "task_name")
private String taskName;
/**
* 用于驳回和自由跳的目标任务标识。
*/
@TableField(value = "target_task_key")
private String targetTaskKey;
/**
* 任务的执行Id。
*/
@TableField(value = "execution_id")
private String executionId;
/**
* 会签任务的执行Id。
*/
@TableField(value = "multi_instance_exec_id")
private String multiInstanceExecId;
/**
* 审批类型。
*/
@TableField(value = "approval_type")
private String approvalType;
/**
* 批注内容。
*/
@TableField(value = "task_comment")
private String taskComment;
/**
* 委托指定人,比如加签、转办等。
*/
@TableField(value = "delegate_assignee")
private String delegateAssignee;
/**
* 自定义数据。开发者可自行扩展推荐使用JSON格式数据。
*/
@TableField(value = "custom_business_data")
private String customBusinessData;
/**
* 审批人头像。
*/
@TableField(value = "head_image_url")
private String headImageUrl;
/**
* 创建者Id。
*/
@TableField(value = "create_user_id")
private Long createUserId;
/**
* 创建者登录名。
*/
@TableField(value = "create_login_name")
private String createLoginName;
/**
* 创建者显示名。
*/
@TableField(value = "create_username")
private String createUsername;
/**
* 创建时间。
*/
@TableField(value = "create_time")
private Date createTime;
private static final String REQ_ATTRIBUTE_KEY = "flowTaskComment";
public FlowTaskComment(TaskInfo task) {
this.fillWith(task);
}
public static void setToRequest(FlowTaskComment comment) {
if (ContextUtil.getHttpRequest() != null) {
ContextUtil.getHttpRequest().setAttribute(REQ_ATTRIBUTE_KEY, comment);
}
}
public static FlowTaskComment getFromRequest() {
if (ContextUtil.getHttpRequest() == null) {
return null;
}
return (FlowTaskComment) ContextUtil.getHttpRequest().getAttribute(REQ_ATTRIBUTE_KEY);
}
public void fillWith(TaskInfo task) {
this.taskId = task.getId();
this.taskKey = task.getTaskDefinitionKey();
this.taskName = task.getName();
this.processInstanceId = task.getProcessInstanceId();
this.executionId = task.getExecutionId();
}
}

View File

@@ -0,0 +1,87 @@
package com.orangeforms.common.flow.model;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
/**
* 流程任务扩展实体对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
@TableName(value = "zz_flow_task_ext")
public class FlowTaskExt {
/**
* 流程引擎的定义Id。
*/
@TableField(value = "process_definition_id")
private String processDefinitionId;
/**
* 流程引擎任务Id。
*/
@TableField(value = "task_id")
private String taskId;
/**
* 操作列表JSON。
*/
@TableField(value = "operation_list_json")
private String operationListJson;
/**
* 变量列表JSON。
*/
@TableField(value = "variable_list_json")
private String variableListJson;
/**
* 存储多实例的assigneeList的JSON。
*/
@TableField(value = "assignee_list_json")
private String assigneeListJson;
/**
* 分组类型。
*/
@TableField(value = "group_type")
private String groupType;
/**
* 保存岗位相关的数据。
*/
@TableField(value = "dept_post_list_json")
private String deptPostListJson;
/**
* 逗号分隔的角色Id。
*/
@TableField(value = "role_ids")
private String roleIds;
/**
* 逗号分隔的部门Id。
*/
@TableField(value = "dept_ids")
private String deptIds;
/**
* 逗号分隔候选用户名。
*/
@TableField(value = "candidate_usernames")
private String candidateUsernames;
/**
* 抄送相关的数据。
*/
@TableField(value = "copy_list_json")
private String copyListJson;
/**
* 用户任务的扩展属性存储为JSON的字符串格式。
*/
@TableField(value = "extra_data_json")
private String extraDataJson;
}

View File

@@ -0,0 +1,163 @@
package com.orangeforms.common.flow.model;
import com.baomidou.mybatisplus.annotation.*;
import com.orangeforms.common.core.annotation.DeptFilterColumn;
import com.orangeforms.common.core.annotation.UserFilterColumn;
import com.orangeforms.common.core.annotation.RelationConstDict;
import com.orangeforms.common.flow.constant.FlowTaskStatus;
import lombok.Data;
import java.util.Date;
import java.util.Map;
/**
* 工作流工单实体对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
@TableName(value = "zz_flow_work_order")
public class FlowWorkOrder {
/**
* 主键Id。
*/
@TableId(value = "work_order_id")
private Long workOrderId;
/**
* 租户Id。
*/
@TableField(value = "tenant_id")
private Long tenantId;
/**
* 应用编码。为空时,表示非第三方应用接入。
*/
@TableField(value = "app_code")
private String appCode;
/**
* 工单编码字段。
*/
@TableField(value = "work_order_code")
private String workOrderCode;
/**
* 流程定义标识。
*/
@TableField(value = "process_definition_key")
private String processDefinitionKey;
/**
* 流程名称。
*/
@TableField(value = "process_definition_name")
private String processDefinitionName;
/**
* 流程引擎的定义Id。
*/
@TableField(value = "process_definition_id")
private String processDefinitionId;
/**
* 流程实例Id。
*/
@TableField(value = "process_instance_id")
private String processInstanceId;
/**
* 在线表单的主表Id。
*/
@TableField(value = "online_table_id")
private Long onlineTableId;
/**
* 静态表单所使用的数据表名。
*/
@TableField(value = "table_name")
private String tableName;
/**
* 业务主键值。
*/
@TableField(value = "business_key")
private String businessKey;
/**
* 最近的审批状态。
*/
@TableField(value = "latest_approval_status")
private Integer latestApprovalStatus;
/**
* 流程状态。参考FlowTaskStatus常量值对象。
*/
@TableField(value = "flow_status")
private Integer flowStatus;
/**
* 提交用户登录名称。
*/
@TableField(value = "submit_username")
private String submitUsername;
/**
* 提交用户所在部门Id。
*/
@DeptFilterColumn
@TableField(value = "dept_id")
private Long deptId;
/**
* 更新时间。
*/
@TableField(value = "update_time")
private Date updateTime;
/**
* 更新者Id。
*/
@TableField(value = "update_user_id")
private Long updateUserId;
/**
* 创建时间。
*/
@TableField(value = "create_time")
private Date createTime;
/**
* 创建者Id。
*/
@UserFilterColumn
@TableField(value = "create_user_id")
private Long createUserId;
/**
* 逻辑删除标记字段(1: 正常 -1: 已删除)。
*/
@TableLogic
@TableField(value = "deleted_flag")
private Integer deletedFlag;
/**
* createTime 范围过滤起始值(>=)。
*/
@TableField(exist = false)
private String createTimeStart;
/**
* createTime 范围过滤结束值(<=)。
*/
@TableField(exist = false)
private String createTimeEnd;
@RelationConstDict(
masterIdField = "flowStatus",
constantDictClass = FlowTaskStatus.class)
@TableField(exist = false)
private Map<String, Object> flowStatusDictMap;
}

View File

@@ -0,0 +1,72 @@
package com.orangeforms.common.flow.model;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;
/**
* 工作流工单扩展数据实体对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
@TableName(value = "zz_flow_work_order_ext")
public class FlowWorkOrderExt {
/**
* 主键Id。
*/
@TableId(value = "id")
private Long id;
/**
* 流程工单Id。
*/
@TableField(value = "work_order_id")
private Long workOrderId;
/**
* 草稿数据。
*/
@TableField(value = "draft_data")
private String draftData;
/**
* 业务数据。
*/
@TableField(value = "business_data")
private String businessData;
/**
* 更新时间。
*/
@TableField(value = "update_time")
private Date updateTime;
/**
* 更新者Id。
*/
@TableField(value = "update_user_id")
private Long updateUserId;
/**
* 创建时间。
*/
@TableField(value = "create_time")
private Date createTime;
/**
* 创建者Id。
*/
@TableField(value = "create_user_id")
private Long createUserId;
/**
* 逻辑删除标记字段(1: 正常 -1: 已删除)。
*/
@TableLogic
@TableField(value = "deleted_flag")
private Integer deletedFlag;
}

View File

@@ -0,0 +1,44 @@
package com.orangeforms.common.flow.model.constant;
import java.util.HashMap;
import java.util.Map;
/**
* 工作流绑定表单类型。
*
* @author Jerry
* @date 2024-07-02
*/
public final class FlowBindFormType {
/**
* 在线表单。
*/
public static final int ONLINE_FORM = 0;
/**
* 路由表单。
*/
public static final int ROUTER_FORM = 1;
private static final Map<Object, String> DICT_MAP = new HashMap<>(2);
static {
DICT_MAP.put(ONLINE_FORM, "在线表单");
DICT_MAP.put(ROUTER_FORM, "路由表单");
}
/**
* 判断参数是否为当前常量字典的合法值。
*
* @param value 待验证的参数值。
* @return 合法返回true否则false。
*/
public static boolean isValid(Integer value) {
return value != null && DICT_MAP.containsKey(value);
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private FlowBindFormType() {
}
}

View File

@@ -0,0 +1,44 @@
package com.orangeforms.common.flow.model.constant;
import java.util.HashMap;
import java.util.Map;
/**
* 工作流状态。
*
* @author Jerry
* @date 2024-07-02
*/
public final class FlowEntryStatus {
/**
* 未发布。
*/
public static final int UNPUBLISHED = 0;
/**
* 已发布。
*/
public static final int PUBLISHED = 1;
private static final Map<Object, String> DICT_MAP = new HashMap<>(2);
static {
DICT_MAP.put(UNPUBLISHED, "未发布");
DICT_MAP.put(PUBLISHED, "已发布");
}
/**
* 判断参数是否为当前常量字典的合法值。
*
* @param value 待验证的参数值。
* @return 合法返回true否则false。
*/
public static boolean isValid(Integer value) {
return value != null && DICT_MAP.containsKey(value);
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private FlowEntryStatus() {
}
}

View File

@@ -0,0 +1,21 @@
package com.orangeforms.common.flow.model.constant;
/**
* 工作流消息操作类型。
*
* @author Jerry
* @date 2024-07-02
*/
public final class FlowMessageOperationType {
/**
* 已读操作。
*/
public static final int READ_FINISHED = 0;
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private FlowMessageOperationType() {
}
}

View File

@@ -0,0 +1,26 @@
package com.orangeforms.common.flow.model.constant;
/**
* 工作流消息类型。
*
* @author Jerry
* @date 2024-07-02
*/
public final class FlowMessageType {
/**
* 催办消息。
*/
public static final int REMIND_TYPE = 0;
/**
* 抄送消息。
*/
public static final int COPY_TYPE = 1;
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private FlowMessageType() {
}
}

View File

@@ -0,0 +1,44 @@
package com.orangeforms.common.flow.model.constant;
import java.util.HashMap;
import java.util.Map;
/**
* 流程变量类型。
*
* @author Jerry
* @date 2024-07-02
*/
public final class FlowVariableType {
/**
* 流程实例变量。
*/
public static final int INSTANCE = 0;
/**
* 任务变量。
*/
public static final int TASK = 1;
private static final Map<Object, String> DICT_MAP = new HashMap<>(2);
static {
DICT_MAP.put(INSTANCE, "流程实例变量");
DICT_MAP.put(TASK, "任务变量");
}
/**
* 判断参数是否为当前常量字典的合法值。
*
* @param value 待验证的参数值。
* @return 合法返回true否则false。
*/
public static boolean isValid(Integer value) {
return value != null && DICT_MAP.containsKey(value);
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private FlowVariableType() {
}
}

View File

@@ -0,0 +1,18 @@
package com.orangeforms.common.flow.object;
import lombok.Data;
/**
* 流程任务的扩展属性。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
public class FlowElementExtProperty {
/**
* 最近的审批状态该值目前仅仅用于流程线元素即SequenceElement。
*/
private Integer latestApprovalStatus;
}

View File

@@ -0,0 +1,37 @@
package com.orangeforms.common.flow.object;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* 流程扩展数据对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
public class FlowEntryExtensionData {
/**
* 通知类型。
*/
private List<String> notifyTypes;
/**
* 流程审批状态字典数据列表。Map的key是id和name。
*/
private List<Map<String, String>> approvalStatusDict;
/**
* 级联删除业务数据。
*/
private Boolean cascadeDeleteBusinessData = false;
/**
* 是否支持流程复活。
*/
private Boolean supportRevive = false;
/**
* 复活数据保留天数。0表示永久保留。
*/
private Integer keptReviveDays = 0;
}

View File

@@ -0,0 +1,24 @@
package com.orangeforms.common.flow.object;
import lombok.Data;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
/**
* 工作流运行时常用对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
public class FlowRumtimeObject {
/**
* 运行时流程实例对象。
*/
private ProcessInstance instance;
/**
* 运行时流程任务对象。
*/
private Task task;
}

View File

@@ -0,0 +1,22 @@
package com.orangeforms.common.flow.object;
import lombok.Data;
/**
* 表示多实例任务的指派人信息。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
public class FlowTaskMultiSignAssign {
/**
* 指派人类型。参考常量类 UserFilterGroup。
*/
private String assigneeType;
/**
* 逗号分隔的指派人列表。
*/
private String assigneeList;
}

View File

@@ -0,0 +1,38 @@
package com.orangeforms.common.flow.object;
import lombok.Data;
/**
* 流程图中的用户任务操作数据。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
public class FlowTaskOperation {
/**
* 操作Id。
*/
private String id;
/**
* 操作的标签名。
*/
private String label;
/**
* 操作类型。
*/
private String type;
/**
* 显示顺序。
*/
private Integer showOrder;
/**
* 最后审批状态。
*/
private Integer latestApprovalStatus;
/**
* 在流程图中定义的多实例会签的指定人员信息。
*/
private FlowTaskMultiSignAssign multiSignAssignee;
}

View File

@@ -0,0 +1,64 @@
package com.orangeforms.common.flow.object;
import com.orangeforms.common.flow.constant.FlowConstant;
import lombok.Data;
import java.util.LinkedList;
import java.util.List;
/**
* 流程任务岗位候选组数据。仅用于流程任务的候选组类型为岗位时。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
public class FlowTaskPostCandidateGroup {
/**
* 唯一值,目前仅前端使用。
*/
private String id;
/**
* 岗位类型。
* 1. 所有部门岗位审批变量,值为 (allDeptPost)。
* 2. 本部门岗位审批变量,值为 (selfDeptPost)。
* 3. 上级部门岗位审批变量,值为 (upDeptPost)。
* 4. 任意部门关联的岗位审批变量,值为 (deptPost)。
*/
private String type;
/**
* 岗位Id。type为(1,2,3)时使用该值。
*/
private String postId;
/**
* 部门岗位Id。type为(4)时使用该值。
*/
private String deptPostId;
public static List<String> buildCandidateGroupList(List<FlowTaskPostCandidateGroup> groupDataList) {
List<String> candidateGroupList = new LinkedList<>();
for (FlowTaskPostCandidateGroup groupData : groupDataList) {
switch (groupData.getType()) {
case FlowConstant.GROUP_TYPE_ALL_DEPT_POST_VAR:
candidateGroupList.add(groupData.getPostId());
break;
case FlowConstant.GROUP_TYPE_DEPT_POST_VAR:
candidateGroupList.add(groupData.getDeptPostId());
break;
case FlowConstant.GROUP_TYPE_SELF_DEPT_POST_VAR:
candidateGroupList.add("${" + FlowConstant.SELF_DEPT_POST_PREFIX + groupData.getPostId() + "}");
break;
case FlowConstant.GROUP_TYPE_SIBLING_DEPT_POST_VAR:
candidateGroupList.add("${" + FlowConstant.SIBLING_DEPT_POST_PREFIX + groupData.getPostId() + "}");
break;
case FlowConstant.GROUP_TYPE_UP_DEPT_POST_VAR:
candidateGroupList.add("${" + FlowConstant.UP_DEPT_POST_PREFIX + groupData.getPostId() + "}");
break;
default:
break;
}
}
return candidateGroupList;
}
}

View File

@@ -0,0 +1,63 @@
package com.orangeforms.common.flow.object;
import lombok.Data;
import java.util.List;
/**
* 流程用户任务扩展数据对象。
*
* @author Jerry
* @date 2024-07-02
*/
@Data
public class FlowUserTaskExtData {
public static final String NOTIFY_TYPE_MSG = "message";
public static final String NOTIFY_TYPE_EMAIL = "email";
public static final String TIMEOUT_AUTO_COMPLETE = "autoComplete";
public static final String TIMEOUT_SEND_MSG = "sendMessage";
public static final String EMPTY_USER_TO_ASSIGNEE = "toAssignee";
public static final String EMPTY_USER_AUTO_REJECT = "autoReject";
public static final String EMPTY_USER_AUTO_COMPLETE = "autoComplete";
/**
* 拒绝后再提交,走重新审批。
*/
public static final String REJECT_TYPE_REDO = "0";
/**
* 拒绝后再提交,直接回到驳回前的节点。
*/
public static final String REJECT_TYPE_BACK_TO_SOURCE = "1";
/**
* 任务通知类型列表。
*/
private List<String> flowNotifyTypeList;
/**
* 拒绝后再次提交的审批类型。
*/
private String rejectType = REJECT_TYPE_REDO;
/**
* 到期提醒的小时数(从待办任务被创建的时候开始计算)。
*/
private Integer timeoutHours;
/**
* 任务超时的处理方式。
*/
private String timeoutHandleWay;
/**
* 默认审批人。
*/
private String defaultAssignee;
/**
* 空用户审批处理方式。
*/
private String emptyUserHandleWay;
/**
* 空用户审批时设定的审批人。
*/
private String emptyUserToAssignee;
}

View File

@@ -0,0 +1,568 @@
package com.orangeforms.common.flow.service;
import com.alibaba.fastjson.JSONObject;
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.Tuple2;
import com.orangeforms.common.flow.model.FlowTaskComment;
import com.orangeforms.common.flow.model.FlowTaskExt;
import com.orangeforms.common.flow.vo.FlowTaskVo;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FieldExtension;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.ExecutionListener;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.task.api.history.HistoricTaskInstance;
import javax.xml.stream.XMLStreamException;
import java.text.ParseException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 流程引擎API的接口封装服务。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowApiService {
/**
* 启动流程实例。
*
* @param processDefinitionId 流程定义Id。
* @param dataId 业务主键Id。
* @return 新启动的流程实例。
*/
ProcessInstance start(String processDefinitionId, Object dataId);
/**
* 完成第一个用户任务。
*
* @param processInstanceId 流程实例Id。
* @param flowTaskComment 审批对象。
* @param taskVariableData 流程任务的变量数据。
* @return 新完成的任务对象。
*/
Task takeFirstTask(String processInstanceId, FlowTaskComment flowTaskComment, JSONObject taskVariableData);
/**
* 启动流程实例如果当前登录用户为第一个用户任务的指派者或者Assginee为流程启动人变量时
* 则自动完成第一个用户任务。
*
* @param processDefinitionId 流程定义Id。
* @param dataId 当前流程主表的主键数据。
* @param flowTaskComment 审批对象。
* @param taskVariableData 流程任务的变量数据。
* @return 新启动的流程实例。
*/
ProcessInstance startAndTakeFirst(
String processDefinitionId, Object dataId, FlowTaskComment flowTaskComment, JSONObject taskVariableData);
/**
* 多实例加签减签。
*
* @param startTaskInstance 会签对象的发起任务实例。
* @param multiInstanceActiveTask 正在执行的多实例任务对象。
* @param newAssignees 新指派人,多个指派人之间逗号分隔。
* @param isAdd 是否为加签。
*/
void submitConsign(HistoricTaskInstance startTaskInstance, Task multiInstanceActiveTask, String newAssignees, boolean isAdd);
/**
* 完成任务,同时提交审批数据。
*
* @param task 工作流任务对象。
* @param flowTaskComment 审批对象。
* @param taskVariableData 流程任务的变量数据。
*/
void completeTask(Task task, FlowTaskComment flowTaskComment, JSONObject taskVariableData);
/**
* 判断当前登录用户是否为流程实例中的用户任务的指派人。或是候选人之一,如果是候选人则拾取该任务并成为指派人。
* 如果都不是,就会返回具体的错误信息。
*
* @param task 流程实例中的用户任务。
* @return 调用结果。
*/
CallResult verifyAssigneeOrCandidateAndClaim(Task task);
/**
* 初始化并返回流程实例的变量Map。
*
* @param processDefinitionId 流程定义Id。
* @return 初始化后的流程实例变量Map。
*/
Map<String, Object> initAndGetProcessInstanceVariables(String processDefinitionId);
/**
* 判断当前登录用户是否为流程实例中的用户任务的指派人。或是候选人之一。
*
* @param task 流程实例中的用户任务。
* @return 是返回true否则false。
*/
boolean isAssigneeOrCandidate(TaskInfo task);
/**
* 获取指定流程定义的全部流程节点。
*
* @param processDefinitionId 流程定义Id。
* @return 当前流程定义的全部节点集合。
*/
Collection<FlowElement> getProcessAllElements(String processDefinitionId);
/**
* 判断当前登录用户是否为流程实例的发起人。
*
* @param processInstanceId 流程实例Id。
* @return 是返回true否则false。
*/
boolean isProcessInstanceStarter(String processInstanceId);
/**
* 为流程实例设置BusinessKey。
*
* @param processInstanceId 流程实例Id。
* @param dataId 通常为主表的主键Id。
*/
void setBusinessKeyForProcessInstance(String processInstanceId, Object dataId);
/**
* 判断指定的流程实例Id是否存在。
*
* @param processInstanceId 流程实例Id。
* @return 存在返回true否则false。
*/
boolean existActiveProcessInstance(String processInstanceId);
/**
* 获取指定的流程实例对象。
*
* @param processInstanceId 流程实例Id。
* @return 流程实例对象。
*/
ProcessInstance getProcessInstance(String processInstanceId);
/**
* 获取指定的流程实例对象。
*
* @param processDefinitionId 流程定义Id。
* @param businessKey 业务主键Id。
* @return 流程实例对象。
*/
ProcessInstance getProcessInstanceByBusinessKey(String processDefinitionId, String businessKey);
/**
* 获取流程实例的列表。
*
* @param processInstanceIdSet 流程实例Id集合。
* @return 流程实例列表。
*/
List<ProcessInstance> getProcessInstanceList(Set<String> processInstanceIdSet);
/**
* 根据流程定义Id查询流程定义对象。
*
* @param processDefinitionId 流程定义Id。
* @return 流程定义对象。
*/
ProcessDefinition getProcessDefinitionById(String processDefinitionId);
/**
* 根据流程部署Id查询流程定义对象。
*
* @param deployId 流程部署Id。
* @return 流程定义对象。
*/
ProcessDefinition getProcessDefinitionByDeployId(String deployId);
/**
* 获取流程定义的列表。
*
* @param processDefinitionIdSet 流程定义Id集合。
* @return 流程定义列表。
*/
List<ProcessDefinition> getProcessDefinitionList(Set<String> processDefinitionIdSet);
/**
* 挂起流程定义对象。
*
* @param processDefinitionId 流程定义Id。
*/
void suspendProcessDefinition(String processDefinitionId);
/**
* 激活流程定义对象。
*
* @param processDefinitionId 流程定义Id。
*/
void activateProcessDefinition(String processDefinitionId);
/**
* 获取指定流程定义的BpmnModel。
*
* @param processDefinitionId 流程定义Id。
* @return 关联的BpmnModel。
*/
BpmnModel getBpmnModelByDefinitionId(String processDefinitionId);
/**
* 判断任务是否为多实例任务。
*
* @param processDefinitionId 流程定义Id。
* @param taskKey 流程任务标识。
* @return true为多实例否则false。
*/
boolean isMultiInstanceTask(String processDefinitionId, String taskKey);
/**
* 设置流程实例的变量集合。
*
* @param processInstanceId 流程实例Id。
* @param variableMap 变量名。
*/
void setProcessInstanceVariables(String processInstanceId, Map<String, Object> variableMap);
/**
* 获取流程实例的变量。
*
* @param processInstanceId 流程实例Id。
* @param variableName 变量名。
* @return 变量值。
*/
Object getProcessInstanceVariable(String processInstanceId, String variableName);
/**
* 获取指定流程实例和任务Id的当前活动任务。
*
* @param processInstanceId 流程实例Id。
* @param taskId 流程任务Id。
* @return 当前流程实例的活动任务。
*/
Task getProcessInstanceActiveTask(String processInstanceId, String taskId);
/**
* 获取指定流程实例的当前活动任务列表。
*
* @param processInstanceId 流程实例Id。
* @return 当前流程实例的活动任务。
*/
List<Task> getProcessInstanceActiveTaskList(String processInstanceId);
/**
* 获取指定流程实例的当前活动任务列表,同时转换为流出任务视图对象列表。
*
* @param processInstanceId 流程实例Id。
* @return 当前流程实例的活动任务。
*/
List<FlowTaskVo> getProcessInstanceActiveTaskListAndConvert(String processInstanceId);
/**
* 根据任务Id获取当前运行时任务。
*
* @param taskId 任务Id。
* @return 运行时任务对象。
*/
Task getTaskById(String taskId);
/**
* 获取用户的任务列表。这其中包括当前用户作为指派人和候选人。
*
* @param username 指派人。
* @param definitionKey 流程定义的标识。
* @param definitionName 流程定义名。
* @param taskName 任务名称。
* @param pageParam 分页对象。
* @return 用户的任务列表。
*/
MyPageData<Task> getTaskListByUserName(
String username, String definitionKey, String definitionName, String taskName, MyPageParam pageParam);
/**
* 获取用户的任务数量。这其中包括当前用户作为指派人和候选人。
*
* @param username 指派人。
* @return 用户的任务数量。
*/
long getTaskCountByUserName(String username);
/**
* 获取流程实例Id集合的运行时任务列表。
*
* @param processInstanceIdSet 流程实例Id集合。
* @return 运行时任务列表。
*/
List<Task> getTaskListByProcessInstanceIds(List<String> processInstanceIdSet);
/**
* 将流程任务列表数据,转换为前端可以显示的流程对象。
*
* @param taskList 流程引擎中的任务列表。
* @return 前端可以显示的流程任务列表。
*/
List<FlowTaskVo> convertToFlowTaskList(List<Task> taskList);
/**
* 添加流程实例结束的监听器。
*
* @param bpmnModel 流程模型。
* @param listenerClazz 流程监听器的Class对象。
*/
void addProcessInstanceEndListener(BpmnModel bpmnModel, Class<? extends ExecutionListener> listenerClazz);
/**
* 添加流程任务的执行监听器。
*
* @param flowElement 指定任务节点。
* @param listenerClazz 执行监听器。
* @param event 事件。
* @param fieldExtensions 执行监听器的扩展变量列表。
*/
void addExecutionListener(
FlowElement flowElement,
Class<? extends ExecutionListener> listenerClazz,
String event,
List<FieldExtension> fieldExtensions);
/**
* 添加流程任务创建的任务监听器。
*
* @param userTask 用户任务。
* @param listenerClazz 任务监听器。
*/
void addTaskCreateListener(UserTask userTask, Class<? extends TaskListener> listenerClazz);
/**
* 获取流程实例的历史流程实例。
*
* @param processInstanceId 流程实例Id。
* @return 历史流程实例。
*/
HistoricProcessInstance getHistoricProcessInstance(String processInstanceId);
/**
* 获取流程实例的历史流程实例列表。
*
* @param processInstanceIdSet 流程实例Id集合。
* @return 历史流程实例列表。
*/
List<HistoricProcessInstance> getHistoricProcessInstanceList(Set<String> processInstanceIdSet);
/**
* 查询历史流程实例的列表。
*
* @param processDefinitionKey 流程标识名。
* @param processDefinitionName 流程名。
* @param startUser 流程发起用户。
* @param beginDate 流程发起开始时间。
* @param endDate 流程发起结束时间。
* @param pageParam 分页对象。
* @param finishedOnly 仅仅返回已经结束的流程。
* @return 分页后的查询列表对象。
* @throws ParseException 日期参数解析失败。
*/
MyPageData<HistoricProcessInstance> getHistoricProcessInstanceList(
String processDefinitionKey,
String processDefinitionName,
String startUser,
String beginDate,
String endDate,
MyPageParam pageParam,
boolean finishedOnly) throws ParseException;
/**
* 获取流程实例的已完成历史任务列表。
*
* @param processInstanceId 流程实例Id。
* @return 流程实例已完成的历史任务列表。
*/
List<HistoricActivityInstance> getHistoricActivityInstanceList(String processInstanceId);
/**
* 获取流程实例的已完成历史任务列表,同时按照每个活动实例的开始时间升序排序。
*
* @param processInstanceId 流程实例Id。
* @return 流程实例已完成的历史任务列表。
*/
List<HistoricActivityInstance> getHistoricActivityInstanceListOrderByStartTime(String processInstanceId);
/**
* 获取当前用户的历史已办理任务列表。
*
* @param processDefinitionName 流程名。
* @param beginDate 流程发起开始时间。
* @param endDate 流程发起结束时间。
* @param pageParam 分页对象。
* @return 分页后的查询列表对象。
* @throws ParseException 日期参数解析失败。
*/
MyPageData<HistoricTaskInstance> getHistoricTaskInstanceFinishedList(
String processDefinitionName,
String beginDate,
String endDate,
MyPageParam pageParam) throws ParseException;
/**
* 获取指定的历史任务实例。
*
* @param processInstanceId 流程实例Id。
* @param taskId 任务Id。
* @return 历史任务实例。
*/
HistoricTaskInstance getHistoricTaskInstance(String processInstanceId, String taskId);
/**
* 获取流程实例的待完成任务列表。
*
* @param processInstanceId 流程实例Id。
* @return 流程实例待完成的任务列表。
*/
List<HistoricActivityInstance> getHistoricUnfinishedInstanceList(String processInstanceId);
/**
* 终止流程实例,将任务从当前节点直接流转到主流程的结束事件。
*
* @param processInstanceId 流程实例Id。
* @param stopReason 停止原因。
* @param forCancel 是否由取消工单触发。
* @return 执行结果。
*/
CallResult stopProcessInstance(String processInstanceId, String stopReason, boolean forCancel);
/**
* 终止流程实例,将任务从当前节点直接流转到主流程的结束事件。
*
* @param processInstanceId 流程实例Id。
* @param stopReason 停止原因。
* @param status 流程状态。
* @return 执行结果。
*/
CallResult stopProcessInstance(String processInstanceId, String stopReason, int status);
/**
* 删除流程实例。
*
* @param processInstanceId 流程实例Id。
*/
void deleteProcessInstance(String processInstanceId);
/**
* 获取任务的指定本地变量。
*
* @param taskId 任务Id。
* @param variableName 变量名。
* @return 变量值。
*/
Object getTaskVariable(String taskId, String variableName);
/**
* 安全的获取任务变量,并返回字符型的变量值。
*
* @param taskId 任务Id。
* @param variableName 变量名。
* @return 返回变量值的字符串形式如果变量不存在不会抛异常返回null。
*/
String getTaskVariableStringWithSafe(String taskId, String variableName);
/**
* 获取任务执行时的指定本地变量。
*
* @param executionId 任务执行时Id。
* @param variableName 变量名。
* @return 变量值。
*/
Object getExecutionVariable(String executionId, String variableName);
/**
* 安全的获取任务执行时变量,并返回字符型的变量值。
*
* @param executionId 任务执行时Id。
* @param variableName 变量名。
* @return 返回变量值的字符串形式如果变量不存在不会抛异常返回null。
*/
String getExecutionVariableStringWithSafe(String executionId, String variableName);
/**
* 获取历史流程变量。
*
* @param processInstanceId 流程实例Id。
* @param variableName 变量名。
* @return 获取历史流程变量。
*/
Object getHistoricProcessInstanceVariable(String processInstanceId, String variableName);
/**
* 将xml格式的流程模型字符串转换为标准的流程模型。
*
* @param bpmnXml xml格式的流程模型字符串。
* @return 转换后的标准的流程模型。
* @throws XMLStreamException XML流处理异常
*/
BpmnModel convertToBpmnModel(String bpmnXml) throws XMLStreamException;
/**
* 回退到上一个用户任务节点。如果没有指定,则回退到上一个任务。
*
* @param task 当前活动任务。
* @param targetKey 指定回退到的任务标识。如果为null则回退到上一个任务。
* @param forReject true表示驳回false为撤回。
* @param reason 驳回或者撤销的原因。
* @return 回退结果。
*/
CallResult backToRuntimeTask(Task task, String targetKey, boolean forReject, String reason);
/**
* 转办任务给他人。
*
* @param task 流程任务。
* @param flowTaskComment 审批对象。
*/
void transferTo(Task task, FlowTaskComment flowTaskComment);
/**
* 获取当前任务在流程图中配置候选用户组数据。
*
* @param flowTaskExt 流程任务扩展对象。
* @param taskId 运行时任务Id。
* @return 候选用户组数据。
*/
List<String> getCandidateUsernames(FlowTaskExt flowTaskExt, String taskId);
/**
* 获取当前任务在流程图中配置到的部门岗位Id集合和岗位Id集合。
*
* @param flowTaskExt 流程任务扩展对象。
* @param processInstanceId 流程实例Id。
* @param historic 是否为历史任务。
* @return first为部门岗位Id集合second是岗位Id集合。
*/
Tuple2<Set<String>, Set<String>> getDeptPostIdAndPostIds(
FlowTaskExt flowTaskExt, String processInstanceId, boolean historic);
/**
* 获取流程图中所有用户任务的映射。
*
* @param processDefinitionId 流程定义Id。
* @return 流程图中所有用户任务的映射。
*/
Map<String, UserTask> getAllUserTaskMap(String processDefinitionId);
/**
* 获取流程图中指定的用户任务。
*
* @param processDefinitionId 流程定义Id。
* @param taskKey 用户任务标识。
* @return 用户任务。
*/
UserTask getUserTask(String processDefinitionId, String taskKey);
}

View File

@@ -0,0 +1,69 @@
package com.orangeforms.common.flow.service;
import com.orangeforms.common.core.base.service.IBaseService;
import com.orangeforms.common.flow.model.*;
import java.util.List;
/**
* FlowCategory数据操作服务接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowCategoryService extends IBaseService<FlowCategory, Long> {
/**
* 保存新增对象。
*
* @param flowCategory 新增对象。
* @return 返回新增对象。
*/
FlowCategory saveNew(FlowCategory flowCategory);
/**
* 更新数据对象。
*
* @param flowCategory 更新的对象。
* @param originalFlowCategory 原有数据对象。
* @return 成功返回true否则false。
*/
boolean update(FlowCategory flowCategory, FlowCategory originalFlowCategory);
/**
* 删除指定数据。
*
* @param categoryId 主键Id。
* @return 成功返回true否则false。
*/
boolean remove(Long categoryId);
/**
* 获取单表查询结果。由于没有关联数据查询,因此在仅仅获取单表数据的场景下,效率更高。
* 如果需要同时获取关联数据,请移步(getFlowCategoryListWithRelation)方法。
*
* @param filter 过滤对象。
* @param orderBy 排序参数。
* @return 查询结果集。
*/
List<FlowCategory> getFlowCategoryList(FlowCategory filter, String orderBy);
/**
* 获取主表的查询结果,以及主表关联的字典数据和一对一从表数据,以及一对一从表的字典数据。
* 该查询会涉及到一对一从表的关联过滤,或一对多从表的嵌套关联过滤,因此性能不如单表过滤。
* 如果仅仅需要获取主表数据,请移步(getFlowCategoryList),以便获取更好的查询性能。
*
* @param filter 主表过滤对象。
* @param orderBy 排序参数。
* @return 查询结果集。
*/
List<FlowCategory> getFlowCategoryListWithRelation(FlowCategory filter, String orderBy);
/**
* 当前流程分类编码是否存在。
*
* @param code 流程分类编码。
* @return true存在否则false。
*/
boolean existByCode(String code);
}

View File

@@ -0,0 +1,133 @@
package com.orangeforms.common.flow.service;
import com.orangeforms.common.core.base.service.IBaseService;
import com.orangeforms.common.flow.model.*;
import javax.xml.stream.XMLStreamException;
import java.util.List;
import java.util.Set;
/**
* FlowEntry数据操作服务接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowEntryService extends IBaseService<FlowEntry, Long> {
/**
* 保存新增对象。
*
* @param flowEntry 新增工作流对象。
* @return 返回新增对象。
*/
FlowEntry saveNew(FlowEntry flowEntry);
/**
* 发布指定流程。
*
* @param flowEntry 待发布的流程对象。
* @param initTaskInfo 第一个非开始节点任务的附加信息。
* @throws XMLStreamException 解析bpmn.xml的异常。
*/
void publish(FlowEntry flowEntry, String initTaskInfo) throws XMLStreamException;
/**
* 更新数据对象。
*
* @param flowEntry 更新的对象。
* @param originalFlowEntry 原有数据对象。
* @return 成功返回true否则false。
*/
boolean update(FlowEntry flowEntry, FlowEntry originalFlowEntry);
/**
* 删除指定数据。
*
* @param entryId 主键Id。
* @return 成功返回true否则false。
*/
boolean remove(Long entryId);
/**
* 获取单表查询结果。由于没有关联数据查询,因此在仅仅获取单表数据的场景下,效率更高。
* 如果需要同时获取关联数据,请移步(getFlowEntryListWithRelation)方法。
*
* @param filter 过滤对象。
* @param orderBy 排序参数。
* @return 查询结果集。
*/
List<FlowEntry> getFlowEntryList(FlowEntry filter, String orderBy);
/**
* 获取主表的查询结果,以及主表关联的字典数据和一对一从表数据,以及一对一从表的字典数据。
* 该查询会涉及到一对一从表的关联过滤,或一对多从表的嵌套关联过滤,因此性能不如单表过滤。
* 如果仅仅需要获取主表数据,请移步(getFlowEntryList),以便获取更好的查询性能。
*
* @param filter 主表过滤对象。
* @param orderBy 排序参数。
* @return 查询结果集。
*/
List<FlowEntry> getFlowEntryListWithRelation(FlowEntry filter, String orderBy);
/**
* 根据流程定义标识获取流程对象。从缓存中读取,如不存在则从数据库读取后,再同步到缓存。
*
* @param processDefinitionKey 流程定义标识。
* @return 流程对象。
*/
FlowEntry getFlowEntryFromCache(String processDefinitionKey);
/**
* 根据流程Id获取流程发布列表数据。
*
* @param entryId 流程Id。
* @return 流程关联的发布列表数据。
*/
List<FlowEntryPublish> getFlowEntryPublishList(Long entryId);
/**
* 根据流程引擎中的流程定义Id集合查询流程发布对象。
*
* @param processDefinitionIdSet 流程引擎中的流程定义Id集合。
* @return 查询结果。
*/
List<FlowEntryPublish> getFlowEntryPublishList(Set<String> processDefinitionIdSet);
/**
* 获取指定工作流发布版本对象。从缓存中读取,如缓存中不存在,从数据库读取并同步缓存。
*
* @param entryPublishId 工作流发布对象Id。
* @return 查询后的对象。
*/
FlowEntryPublish getFlowEntryPublishFromCache(Long entryPublishId);
/**
* 为指定工作流更新发布的主版本。
*
* @param flowEntry 工作流对象。
* @param newMainFlowEntryPublish 工作流新的发布主版本对象。
*/
void updateFlowEntryMainVersion(FlowEntry flowEntry, FlowEntryPublish newMainFlowEntryPublish);
/**
* 挂起指定的工作流发布对象。
*
* @param flowEntryPublish 待挂起的工作流发布对象。
*/
void suspendFlowEntryPublish(FlowEntryPublish flowEntryPublish);
/**
* 激活指定的工作流发布对象。
*
* @param flowEntryPublish 待恢复的工作流发布对象。
*/
void activateFlowEntryPublish(FlowEntryPublish flowEntryPublish);
/**
* 判断指定流程定义标识是否存在。
* @param processDefinitionKey 流程定义标识。
* @return true存在否则false。
*/
boolean existByProcessDefinitionKey(String processDefinitionKey);
}

View File

@@ -0,0 +1,68 @@
package com.orangeforms.common.flow.service;
import com.orangeforms.common.flow.model.*;
import com.orangeforms.common.core.base.service.IBaseService;
import java.util.*;
/**
* 流程变量数据操作服务接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowEntryVariableService extends IBaseService<FlowEntryVariable, Long> {
/**
* 保存新增对象。
*
* @param flowEntryVariable 新增对象。
* @return 返回新增对象。
*/
FlowEntryVariable saveNew(FlowEntryVariable flowEntryVariable);
/**
* 更新数据对象。
*
* @param flowEntryVariable 更新的对象。
* @param originalFlowEntryVariable 原有数据对象。
* @return 成功返回true否则false。
*/
boolean update(FlowEntryVariable flowEntryVariable, FlowEntryVariable originalFlowEntryVariable);
/**
* 删除指定数据。
*
* @param variableId 主键Id。
* @return 成功返回true否则false。
*/
boolean remove(Long variableId);
/**
* 删除指定流程Id的所有变量。
*
* @param entryId 流程Id。
*/
void removeByEntryId(Long entryId);
/**
* 获取单表查询结果。由于没有关联数据查询,因此在仅仅获取单表数据的场景下,效率更高。
* 如果需要同时获取关联数据,请移步(getFlowEntryVariableListWithRelation)方法。
*
* @param filter 过滤对象。
* @param orderBy 排序参数。
* @return 查询结果集。
*/
List<FlowEntryVariable> getFlowEntryVariableList(FlowEntryVariable filter, String orderBy);
/**
* 获取主表的查询结果,以及主表关联的字典数据和一对一从表数据,以及一对一从表的字典数据。
* 该查询会涉及到一对一从表的关联过滤,或一对多从表的嵌套关联过滤,因此性能不如单表过滤。
* 如果仅仅需要获取主表数据,请移步(getFlowEntryVariableList),以便获取更好的查询性能。
*
* @param filter 主表过滤对象。
* @param orderBy 排序参数。
* @return 查询结果集。
*/
List<FlowEntryVariable> getFlowEntryVariableListWithRelation(FlowEntryVariable filter, String orderBy);
}

View File

@@ -0,0 +1,106 @@
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;
/**
* 工作流消息数据操作服务接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowMessageService extends IBaseService<FlowMessage, Long> {
/**
* 保存新增对象。
*
* @param flowMessage 新增对象。
* @return 保存后的消息对象。
*/
FlowMessage saveNew(FlowMessage flowMessage);
/**
* 根据工单参数,保存催单消息对象。如果当前工单存在多个待办任务,则插入多条催办消息数据。
*
* @param flowWorkOrder 待催办的工单。
*/
void saveNewRemindMessage(FlowWorkOrder flowWorkOrder);
/**
* 保存抄送消息对象。
*
* @param task 待抄送的任务。
* @param copyDataJson 抄送人员或者组的Id数据。
*/
void saveNewCopyMessage(Task task, JSONObject copyDataJson);
/**
* 更新指定运行时任务Id的消费为已完成状态。
*
* @param taskId 运行时任务Id。
*/
void updateFinishedStatusByTaskId(String taskId);
/**
* 更新指定流程实例Id的消费为已完成状态。
*
* @param processInstanceId 流程实例IdId。
*/
void updateFinishedStatusByProcessInstanceId(String processInstanceId);
/**
* 获取当前用户的催办消息列表。
*
* @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);
}

View File

@@ -0,0 +1,38 @@
package com.orangeforms.common.flow.service;
import com.orangeforms.common.core.base.service.IBaseService;
import com.orangeforms.common.flow.model.FlowMultiInstanceTrans;
/**
* 会签任务操作流水数据操作服务接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowMultiInstanceTransService extends IBaseService<FlowMultiInstanceTrans, Long> {
/**
* 保存新增对象。
*
* @param flowMultiInstanceTrans 新增对象。
* @return 返回新增对象。
*/
FlowMultiInstanceTrans saveNew(FlowMultiInstanceTrans flowMultiInstanceTrans);
/**
* 根据流程执行Id获取对象。
*
* @param executionId 流程执行Id。
* @param taskId 执行任务Id。
* @return 数据对象。
*/
FlowMultiInstanceTrans getByExecutionId(String executionId, String taskId);
/**
* 根据多实例的统一执行Id获取assgineeList字段不为空的数据。
*
* @param multiInstanceExecId 多实例统一执行Id。
* @return 数据对象。
*/
FlowMultiInstanceTrans getWithAssigneeListByMultiInstanceExecId(String multiInstanceExecId);
}

View File

@@ -0,0 +1,84 @@
package com.orangeforms.common.flow.service;
import com.orangeforms.common.core.base.service.IBaseService;
import com.orangeforms.common.flow.model.FlowTaskComment;
import java.util.List;
import java.util.Set;
/**
* 流程任务批注数据操作服务接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowTaskCommentService extends IBaseService<FlowTaskComment, Long> {
/**
* 保存新增对象。
*
* @param flowTaskComment 新增对象。
* @return 返回新增对象。
*/
FlowTaskComment saveNew(FlowTaskComment flowTaskComment);
/**
* 查询指定流程实例Id下的所有审批任务的批注。
*
* @param processInstanceId 流程实例Id。
* @return 查询结果集。
*/
List<FlowTaskComment> getFlowTaskCommentList(String processInstanceId);
/**
* 查询与指定流程任务Id集合关联的所有审批任务的批注。
*
* @param taskIdSet 流程任务Id集合。
* @return 查询结果集。
*/
List<FlowTaskComment> getFlowTaskCommentListByTaskIds(Set<String> taskIdSet);
/**
* 获取指定流程实例的最后一条审批任务。
*
* @param processInstanceId 流程实例Id。
* @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);
/**
* 获取指定任务实例和执行批次的审批数据列表。
*
* @param processInstanceId 流程实例。
* @param taskId 任务Id
* @param executionId 任务执行Id
* @return 审批数据列表。
*/
List<FlowTaskComment> getFlowTaskCommentListByExecutionId(
String processInstanceId, String taskId, String executionId);
/**
* 根据多实例执行Id获取任务审批对象数据列表。
*
* @param multiInstanceExecId 多实例执行Id。
* @return 审批数据列表。
*/
List<FlowTaskComment> getFlowTaskCommentListByMultiInstanceExecId(String multiInstanceExecId);
}

View File

@@ -0,0 +1,124 @@
package com.orangeforms.common.flow.service;
import com.alibaba.fastjson.JSONObject;
import com.orangeforms.common.flow.model.*;
import com.orangeforms.common.flow.object.FlowElementExtProperty;
import com.orangeforms.common.flow.vo.FlowUserInfoVo;
import com.orangeforms.common.core.base.service.IBaseService;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.ExtensionElement;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.task.api.TaskInfo;
import java.util.List;
import java.util.Map;
/**
* 流程任务扩展数据操作服务接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowTaskExtService extends IBaseService<FlowTaskExt, String> {
/**
* 批量插入流程任务扩展信息列表。
*
* @param flowTaskExtList 流程任务扩展信息列表。
*/
void saveBatch(List<FlowTaskExt> flowTaskExtList);
/**
* 查询指定的流程任务扩展对象。
*
* @param processDefinitionId 流程引擎的定义Id。
* @param taskId 流程引擎的任务Id。
* @return 查询结果。
*/
FlowTaskExt getByProcessDefinitionIdAndTaskId(String processDefinitionId, String taskId);
/**
* 查询指定的流程定义的任务扩展对象。
*
* @param processDefinitionId 流程引擎的定义Id。
* @return 查询结果。
*/
List<FlowTaskExt> getByProcessDefinitionId(String processDefinitionId);
/**
* 获取任务扩展信息中的候选人用户信息列表。
*
* @param processInstanceId 流程引擎的实例Id。
* @param flowTaskExt 任务扩展对象。
* @param taskInfo 任务信息。
* @param isMultiInstanceTask 是否为多实例任务。
* @param historic 是否为历史任务。
* @return 候选人用户信息列表。
*/
List<FlowUserInfoVo> getCandidateUserInfoList(
String processInstanceId,
FlowTaskExt flowTaskExt,
TaskInfo taskInfo,
boolean isMultiInstanceTask,
boolean historic);
/**
* 获取指定任务的用户列表信息。
*
* @param processInstanceId 流程实例。
* @param executionId 执行实例。
* @param flowTaskExt 流程用户任务的扩展对象。
* @return 候选人用户信息列表。
*/
List<FlowUserInfoVo> getCandidateUserInfoList(
String processInstanceId,
String executionId,
FlowTaskExt flowTaskExt);
/**
* 通过UserTask对象中的扩展节点信息构建FLowTaskExt对象。
*
* @param userTask 流程图中定义的用户任务对象。
* @return 构建后的流程任务扩展信息对象。
*/
FlowTaskExt buildTaskExtByUserTask(UserTask userTask);
/**
* 获取指定流程图中所有UserTask对象的扩展节点信息构建FLowTaskExt对象列表。
*
* @param bpmnModel 流程图模型对象。
* @return 当前流程图中所有用户流程任务的扩展信息对象列表。
*/
List<FlowTaskExt> buildTaskExtList(BpmnModel bpmnModel);
/**
* 根据流程定义中用户任务的扩展节点数据,构建出前端所需的操作列表数据对象。
* @param extensionMap 用户任务的扩展节点。
* @return 前端所需的操作列表数据对象。
*/
List<JSONObject> buildOperationListExtensionElement(Map<String, List<ExtensionElement>> extensionMap);
/**
* 根据流程定义中用户任务的扩展节点数据,构建出前端所需的变量列表数据对象。
* @param extensionMap 用户任务的扩展节点。
* @return 前端所需的变量列表数据对象。
*/
List<JSONObject> buildVariableListExtensionElement(Map<String, List<ExtensionElement>> extensionMap);
/**
* 读取流程定义中,流程元素的扩展属性数据。
*
* @param element 流程图中定义的流程元素。
* @return 流程元素的扩展属性数据。
*/
FlowElementExtProperty buildFlowElementExt(FlowElement element);
/**
* 读取流程定义中,流程元素的扩展属性数据。
*
* @param element 流程图中定义的流程元素。
* @return 流程元素的扩展属性数据并转换为JSON对象。
*/
JSONObject buildFlowElementExtToJson(FlowElement element);
}

View File

@@ -0,0 +1,184 @@
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.core.object.MyOrderParam;
import com.orangeforms.common.core.object.MyPageData;
import com.orangeforms.common.core.object.MyPageParam;
import com.orangeforms.common.flow.dto.FlowWorkOrderDto;
import com.orangeforms.common.flow.model.FlowWorkOrder;
import com.orangeforms.common.flow.model.FlowWorkOrderExt;
import com.orangeforms.common.flow.vo.FlowWorkOrderVo;
import org.flowable.engine.runtime.ProcessInstance;
import java.util.*;
/**
* 工作流工单表数据操作服务接口。
*
* @author Jerry
* @date 2024-07-02
*/
public interface FlowWorkOrderService extends IBaseService<FlowWorkOrder, Long> {
/**
* 保存新增对象。
*
* @param instance 流程实例对象。
* @param dataId 流程实例的BusinessKey。
* @param onlineTableId 在线数据表的主键Id。
* @param tableName 面向静态表单所使用的表名。
* @return 新增的工作流工单对象。
*/
FlowWorkOrder saveNew(ProcessInstance instance, Object dataId, Long onlineTableId, String tableName);
/**
* 保存工单草稿。
*
* @param instance 流程实例对象。
* @param onlineTableId 在线表单的主表Id。
* @param tableName 静态表单的主表表名。
* @param masterData 主表数据。
* @param slaveData 从表数据。
* @return 工单对象。
*/
FlowWorkOrder saveNewWithDraft(
ProcessInstance instance, Long onlineTableId, String tableName, String masterData, String slaveData);
/**
* 更新流程工单的草稿数据。
*
* @param workOrderId 工单Id。
* @param masterData 主表数据。
* @param slaveData 从表数据。
*/
void updateDraft(Long workOrderId, String masterData, String slaveData);
/**
* 删除指定数据。
*
* @param workOrderId 主键Id。
* @return 成功返回true否则false。
*/
boolean remove(Long workOrderId);
/**
* 删除指定流程实例Id的关联工单。
*
* @param processInstanceId 流程实例Id。
*/
void removeByProcessInstanceId(String processInstanceId);
/**
* 获取工作流工单单表查询结果。
*
* @param filter 过滤对象。
* @param orderBy 排序参数。
* @return 查询结果集。
*/
List<FlowWorkOrder> getFlowWorkOrderList(FlowWorkOrder filter, String orderBy);
/**
* 获取工作流工单列表及其关联字典数据。
*
* @param filter 过滤对象。
* @param orderBy 排序参数。
* @return 查询结果集。
*/
List<FlowWorkOrder> getFlowWorkOrderListWithRelation(FlowWorkOrder filter, String orderBy);
/**
* 根据流程实例Id查询关联的工单对象。
*
* @param processInstanceId 流程实例Id。
* @return 工作流工单对象。
*/
FlowWorkOrder getFlowWorkOrderByProcessInstanceId(String processInstanceId);
/**
* 根据业务主键,查询是否存在指定的工单。
*
* @param tableName 静态表单工作流使用的数据表。
* @param businessKey 业务数据主键Id。
* @param unfinished 是否为没有结束工单。
* @return 存在返回true否则false。
*/
boolean existByBusinessKey(String tableName, Object businessKey, boolean unfinished);
/**
* 根据流程定义和业务主键,查询是否存在指定的未完成工单。
*
* @param processDefinitionKey 静态表单工作流使用的数据表。
* @param businessKey 业务数据主键Id。
* @return 存在返回true否则false。
*/
boolean existUnfinished(String processDefinitionKey, Object businessKey);
/**
* 根据流程实例Id更新流程状态。
*
* @param processInstanceId 流程实例Id。
* @param flowStatus 新的流程状态值如果该值为null不执行任何更新。
*/
void updateFlowStatusByProcessInstanceId(String processInstanceId, Integer flowStatus);
/**
* 根据流程实例Id更新流程最后审批状态。
*
* @param processInstanceId 流程实例Id。
* @param approvalStatus 新的流程最后审批状态如果该值为null不执行任何更新。
*/
void updateLatestApprovalStatusByProcessInstanceId(String processInstanceId, Integer approvalStatus);
/**
* 是否有查看该工单的数据权限。
*
* @param processInstanceId 流程实例Id。
* @return 存在返回true否则false。
*/
boolean hasDataPermOnFlowWorkOrder(String processInstanceId);
/**
* 根据工单列表中的submitUserName找到映射的userShowName并会写到Vo中指定字段。
* 同时这也是一个如何通过插件方法将loginName映射到showName的示例
*
* @param dataList 工单Vo对象列表。
*/
void fillUserShowNameByLoginName(List<FlowWorkOrderVo> dataList);
/**
* 根据工单Id获取工单扩展对象数据。
*
* @param workOrderId 工单Id。
* @return 工单扩展对象。
*/
FlowWorkOrderExt getFlowWorkOrderExtByWorkOrderId(Long workOrderId);
/**
* 根据工单Id集合获取工单扩展对象数据列表。
*
* @param workOrderIds 工单Id集合。
* @return 工单扩展对象列表。
*/
List<FlowWorkOrderExt> getFlowWorkOrderExtByWorkOrderIds(Set<Long> workOrderIds);
/**
* 移除草稿工单,同时停止已经启动的流程实例。
*
* @param flowWorkOrder 工单对象。
* @return 停止流程实例的结果。
*/
CallResult removeDraft(FlowWorkOrder flowWorkOrder);
/**
* 获取分页后的工单列表同时构建部分任务数据。该方法主要是为了尽量减少路由表单工作流listWorkOrder的重复代码。
*
* @param filter 工单过滤对象。
* @param pageParam 分页参数对象。
* @param orderParam 排序参数对象。
* @param processDefinitionKey 流程定义标识。
* @return 分页的工单列表。
*/
MyPageData<FlowWorkOrderVo> getPagedWorkOrderListAndBuildData(
FlowWorkOrderDto filter, MyPageParam pageParam, MyOrderParam orderParam, String processDefinitionKey);
}

View File

@@ -0,0 +1,131 @@
package com.orangeforms.common.flow.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.pagehelper.Page;
import com.orangeforms.common.core.annotation.MyDataSourceResolver;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.core.base.service.BaseService;
import com.orangeforms.common.core.constant.ApplicationConstant;
import com.orangeforms.common.core.object.MyRelationParam;
import com.orangeforms.common.core.object.TokenData;
import com.orangeforms.common.core.util.DefaultDataSourceResolver;
import com.orangeforms.common.sequence.wrapper.IdGeneratorWrapper;
import com.orangeforms.common.flow.dao.*;
import com.orangeforms.common.flow.model.*;
import com.orangeforms.common.flow.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.Set;
@Slf4j
@MyDataSourceResolver(
resolver = DefaultDataSourceResolver.class,
intArg = ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE)
@Service("flowCategoryService")
public class FlowCategoryServiceImpl extends BaseService<FlowCategory, Long> implements FlowCategoryService {
@Autowired
private FlowCategoryMapper flowCategoryMapper;
@Autowired
private IdGeneratorWrapper idGenerator;
/**
* 返回当前Service的主表Mapper对象。
*
* @return 主表Mapper对象。
*/
@Override
protected BaseDaoMapper<FlowCategory> mapper() {
return flowCategoryMapper;
}
@Transactional(rollbackFor = Exception.class)
@Override
public FlowCategory saveNew(FlowCategory flowCategory) {
flowCategory.setCategoryId(idGenerator.nextLongId());
TokenData tokenData = TokenData.takeFromRequest();
flowCategory.setAppCode(tokenData.getAppCode());
flowCategory.setTenantId(tokenData.getTenantId());
flowCategory.setUpdateUserId(tokenData.getUserId());
flowCategory.setCreateUserId(tokenData.getUserId());
Date now = new Date();
flowCategory.setUpdateTime(now);
flowCategory.setCreateTime(now);
flowCategoryMapper.insert(flowCategory);
return flowCategory;
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean update(FlowCategory flowCategory, FlowCategory originalFlowCategory) {
TokenData tokenData = TokenData.takeFromRequest();
flowCategory.setAppCode(tokenData.getAppCode());
flowCategory.setTenantId(tokenData.getTenantId());
flowCategory.setUpdateUserId(tokenData.getUserId());
flowCategory.setCreateUserId(originalFlowCategory.getCreateUserId());
flowCategory.setUpdateTime(new Date());
flowCategory.setCreateTime(originalFlowCategory.getCreateTime());
// 这里重点提示,在执行主表数据更新之前,如果有哪些字段不支持修改操作,请用原有数据对象字段替换当前数据字段。
UpdateWrapper<FlowCategory> uw =
this.createUpdateQueryForNullValue(flowCategory, flowCategory.getCategoryId());
return flowCategoryMapper.update(flowCategory, uw) == 1;
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean remove(Long categoryId) {
return flowCategoryMapper.deleteById(categoryId) == 1;
}
@Override
public List<FlowCategory> getFlowCategoryList(FlowCategory filter, String orderBy) {
if (filter == null) {
filter = new FlowCategory();
}
TokenData tokenData = TokenData.takeFromRequest();
filter.setTenantId(tokenData.getTenantId());
filter.setAppCode(tokenData.getAppCode());
return flowCategoryMapper.getFlowCategoryList(filter, orderBy);
}
@Override
public List<FlowCategory> getFlowCategoryListWithRelation(FlowCategory filter, String orderBy) {
List<FlowCategory> resultList = this.getFlowCategoryList(filter, orderBy);
// 在缺省生成的代码中如果查询结果resultList不是Page对象说明没有分页那么就很可能是数据导出接口调用了当前方法。
// 为了避免一次性的大量数据关联,规避因此而造成的系统运行性能冲击,这里手动进行了分批次读取,开发者可按需修改该值。
int batchSize = resultList instanceof Page ? 0 : 1000;
this.buildRelationForDataList(resultList, MyRelationParam.normal(), batchSize);
return resultList;
}
@Override
public boolean existByCode(String code) {
FlowCategory filter = new FlowCategory();
filter.setCode(code);
return CollUtil.isNotEmpty(this.getFlowCategoryList(filter, null));
}
@Override
public List<FlowCategory> getInList(Set<Long> categoryIds) {
LambdaQueryWrapper<FlowCategory> qw = new LambdaQueryWrapper<>();
qw.in(FlowCategory::getCategoryId, categoryIds);
TokenData tokenData = TokenData.takeFromRequest();
if (tokenData.getAppCode() == null) {
qw.isNull(FlowCategory::getAppCode);
} else {
qw.eq(FlowCategory::getAppCode, tokenData.getAppCode());
}
if (tokenData.getTenantId() == null) {
qw.isNull(FlowCategory::getTenantId);
} else {
qw.eq(FlowCategory::getTenantId, tokenData.getTenantId());
}
return flowCategoryMapper.selectList(qw);
}
}

View File

@@ -0,0 +1,490 @@
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.JSONObject;
import com.github.pagehelper.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orangeforms.common.core.annotation.MyDataSourceResolver;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.core.base.service.BaseService;
import com.orangeforms.common.core.constant.ApplicationConstant;
import com.orangeforms.common.core.object.CallResult;
import com.orangeforms.common.core.object.MyRelationParam;
import com.orangeforms.common.core.object.TokenData;
import com.orangeforms.common.core.util.DefaultDataSourceResolver;
import com.orangeforms.common.core.util.MyModelUtil;
import com.orangeforms.common.flow.constant.FlowConstant;
import com.orangeforms.common.flow.dao.FlowEntryMapper;
import com.orangeforms.common.flow.dao.FlowEntryPublishMapper;
import com.orangeforms.common.flow.dao.FlowEntryPublishVariableMapper;
import com.orangeforms.common.flow.listener.*;
import com.orangeforms.common.flow.model.*;
import com.orangeforms.common.flow.model.constant.FlowEntryStatus;
import com.orangeforms.common.flow.model.constant.FlowVariableType;
import com.orangeforms.common.flow.object.FlowElementExtProperty;
import com.orangeforms.common.flow.object.FlowEntryExtensionData;
import com.orangeforms.common.flow.object.FlowTaskPostCandidateGroup;
import com.orangeforms.common.flow.object.FlowUserTaskExtData;
import com.orangeforms.common.flow.service.*;
import com.orangeforms.common.flow.util.BaseFlowIdentityExtHelper;
import com.orangeforms.common.flow.util.FlowCustomExtFactory;
import com.orangeforms.common.flow.util.FlowRedisKeyUtil;
import com.orangeforms.common.redis.util.CommonRedisUtil;
import com.orangeforms.common.sequence.wrapper.IdGeneratorWrapper;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@MyDataSourceResolver(
resolver = DefaultDataSourceResolver.class,
intArg = ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE)
@Service("flowEntryService")
public class FlowEntryServiceImpl extends BaseService<FlowEntry, Long> implements FlowEntryService {
@Autowired
private FlowEntryMapper flowEntryMapper;
@Autowired
private FlowEntryPublishMapper flowEntryPublishMapper;
@Autowired
private FlowEntryPublishVariableMapper flowEntryPublishVariableMapper;
@Autowired
private FlowEntryVariableService flowEntryVariableService;
@Autowired
private FlowCategoryService flowCategoryService;
@Autowired
private FlowTaskExtService flowTaskExtService;
@Autowired
private FlowApiService flowApiService;
@Autowired
private FlowCustomExtFactory flowCustomExtFactory;
@Autowired
private RepositoryService repositoryService;
@Autowired
private IdGeneratorWrapper idGenerator;
@Autowired
private CommonRedisUtil commonRedisUtil;
private static final Integer FLOW_ENTRY_PUBLISH_TTL = 60 * 60 * 24;
/**
* 返回当前Service的主表Mapper对象。
*
* @return 主表Mapper对象。
*/
@Override
protected BaseDaoMapper<FlowEntry> mapper() {
return flowEntryMapper;
}
@Transactional(rollbackFor = Exception.class)
@Override
public FlowEntry saveNew(FlowEntry flowEntry) {
flowEntry.setEntryId(idGenerator.nextLongId());
flowEntry.setStatus(FlowEntryStatus.UNPUBLISHED);
TokenData tokenData = TokenData.takeFromRequest();
flowEntry.setAppCode(tokenData.getAppCode());
flowEntry.setTenantId(tokenData.getTenantId());
flowEntry.setUpdateUserId(tokenData.getUserId());
flowEntry.setCreateUserId(tokenData.getUserId());
Date now = new Date();
flowEntry.setUpdateTime(now);
flowEntry.setCreateTime(now);
flowEntryMapper.insert(flowEntry);
this.insertBuiltinEntryVariables(flowEntry.getEntryId());
return flowEntry;
}
@Transactional(rollbackFor = Exception.class)
@Override
public void publish(FlowEntry flowEntry, String initTaskInfo) throws XMLStreamException {
commonRedisUtil.evictFormCache(
FlowRedisKeyUtil.makeFlowEntryKey(flowEntry.getProcessDefinitionKey()));
FlowCategory flowCategory = flowCategoryService.getById(flowEntry.getCategoryId());
InputStream xmlStream = new ByteArrayInputStream(
flowEntry.getBpmnXml().getBytes(StandardCharsets.UTF_8));
@Cleanup XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(xmlStream);
BpmnXMLConverter converter = new BpmnXMLConverter();
BpmnModel bpmnModel = converter.convertToBpmnModel(reader);
bpmnModel.getMainProcess().setName(flowEntry.getProcessDefinitionName());
bpmnModel.getMainProcess().setId(flowEntry.getProcessDefinitionKey());
flowApiService.addProcessInstanceEndListener(bpmnModel, FlowFinishedListener.class);
List<FlowTaskExt> flowTaskExtList = flowTaskExtService.buildTaskExtList(bpmnModel);
if (StrUtil.isNotBlank(flowEntry.getExtensionData())) {
FlowEntryExtensionData flowEntryExtensionData =
JSON.parseObject(flowEntry.getExtensionData(), FlowEntryExtensionData.class);
this.mergeTaskNotifyData(flowEntryExtensionData, flowTaskExtList);
}
this.processFlowTaskExtList(flowTaskExtList, bpmnModel);
TokenData tokenData = TokenData.takeFromRequest();
Deployment deploy = repositoryService.createDeployment()
.addBpmnModel(flowEntry.getProcessDefinitionKey() + ".bpmn", bpmnModel)
.tenantId(tokenData.getTenantId() != null ? tokenData.getTenantId().toString() : tokenData.getAppCode())
.name(flowEntry.getProcessDefinitionName())
.key(flowEntry.getProcessDefinitionKey())
.category(flowCategory.getCode())
.deploy();
ProcessDefinition processDefinition = flowApiService.getProcessDefinitionByDeployId(deploy.getId());
FlowEntryPublish flowEntryPublish = new FlowEntryPublish();
flowEntryPublish.setEntryPublishId(idGenerator.nextLongId());
flowEntryPublish.setEntryId(flowEntry.getEntryId());
flowEntryPublish.setProcessDefinitionId(processDefinition.getId());
flowEntryPublish.setDeployId(processDefinition.getDeploymentId());
flowEntryPublish.setPublishVersion(processDefinition.getVersion());
flowEntryPublish.setActiveStatus(true);
flowEntryPublish.setMainVersion(flowEntry.getStatus().equals(FlowEntryStatus.UNPUBLISHED));
flowEntryPublish.setCreateUserId(TokenData.takeFromRequest().getUserId());
flowEntryPublish.setPublishTime(new Date());
flowEntryPublish.setInitTaskInfo(initTaskInfo);
flowEntryPublish.setExtensionData(flowEntry.getExtensionData());
flowEntryPublishMapper.insert(flowEntryPublish);
FlowEntry updatedFlowEntry = new FlowEntry();
updatedFlowEntry.setEntryId(flowEntry.getEntryId());
updatedFlowEntry.setStatus(FlowEntryStatus.PUBLISHED);
updatedFlowEntry.setLatestPublishTime(new Date());
// 对于从未发布过的工作,第一次发布的时候会将本地发布置位主版本。
if (flowEntry.getStatus().equals(FlowEntryStatus.UNPUBLISHED)) {
updatedFlowEntry.setMainEntryPublishId(flowEntryPublish.getEntryPublishId());
}
flowEntryMapper.updateById(updatedFlowEntry);
FlowEntryVariable flowEntryVariableFilter = new FlowEntryVariable();
flowEntryVariableFilter.setEntryId(flowEntry.getEntryId());
List<FlowEntryVariable> flowEntryVariableList =
flowEntryVariableService.getFlowEntryVariableList(flowEntryVariableFilter, null);
if (CollUtil.isNotEmpty(flowTaskExtList)) {
flowTaskExtList.forEach(t -> t.setProcessDefinitionId(processDefinition.getId()));
flowTaskExtService.saveBatch(flowTaskExtList);
}
this.insertEntryPublishVariables(flowEntryVariableList, flowEntryPublish.getEntryPublishId());
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean update(FlowEntry flowEntry, FlowEntry originalFlowEntry) {
commonRedisUtil.evictFormCache(
FlowRedisKeyUtil.makeFlowEntryKey(flowEntry.getProcessDefinitionKey()));
TokenData tokenData = TokenData.takeFromRequest();
flowEntry.setAppCode(tokenData.getAppCode());
flowEntry.setTenantId(tokenData.getTenantId());
flowEntry.setUpdateUserId(tokenData.getUserId());
flowEntry.setCreateUserId(originalFlowEntry.getCreateUserId());
flowEntry.setUpdateTime(new Date());
flowEntry.setCreateTime(originalFlowEntry.getCreateTime());
flowEntry.setPageId(originalFlowEntry.getPageId());
return flowEntryMapper.updateById(flowEntry) == 1;
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean remove(Long entryId) {
FlowEntry flowEntry = this.getById(entryId);
if (flowEntry != null) {
commonRedisUtil.evictFormCache(
FlowRedisKeyUtil.makeFlowEntryKey(flowEntry.getProcessDefinitionKey()));
}
if (flowEntryMapper.deleteById(entryId) != 1) {
return false;
}
flowEntryVariableService.removeByEntryId(entryId);
return true;
}
@Override
public List<FlowEntry> getFlowEntryList(FlowEntry filter, String orderBy) {
if (filter == null) {
filter = new FlowEntry();
}
TokenData tokenData = TokenData.takeFromRequest();
filter.setTenantId(tokenData.getTenantId());
filter.setAppCode(tokenData.getAppCode());
return flowEntryMapper.getFlowEntryList(filter, orderBy);
}
@Override
public List<FlowEntry> getFlowEntryListWithRelation(FlowEntry filter, String orderBy) {
List<FlowEntry> resultList = this.getFlowEntryList(filter, orderBy);
// 在缺省生成的代码中如果查询结果resultList不是Page对象说明没有分页那么就很可能是数据导出接口调用了当前方法。
// 为了避免一次性的大量数据关联,规避因此而造成的系统运行性能冲击,这里手动进行了分批次读取,开发者可按需修改该值。
int batchSize = resultList instanceof Page ? 0 : 1000;
this.buildRelationForDataList(resultList, MyRelationParam.normal(), batchSize);
Set<Long> mainEntryPublishIdSet = resultList.stream()
.map(FlowEntry::getMainEntryPublishId).filter(Objects::nonNull).collect(Collectors.toSet());
if (CollUtil.isNotEmpty(mainEntryPublishIdSet)) {
List<FlowEntryPublish> mainEntryPublishList =
flowEntryPublishMapper.selectBatchIds(mainEntryPublishIdSet);
MyModelUtil.makeOneToOneRelation(FlowEntry.class, resultList, FlowEntry::getMainEntryPublishId,
mainEntryPublishList, FlowEntryPublish::getEntryPublishId, "mainFlowEntryPublish");
}
return resultList;
}
@Override
public FlowEntry getFlowEntryFromCache(String processDefinitionKey) {
String key = FlowRedisKeyUtil.makeFlowEntryKey(processDefinitionKey);
LambdaQueryWrapper<FlowEntry> qw = new LambdaQueryWrapper<>();
qw.eq(FlowEntry::getProcessDefinitionKey, processDefinitionKey);
TokenData tokenData = TokenData.takeFromRequest();
if (StrUtil.isNotBlank(tokenData.getAppCode())) {
qw.eq(FlowEntry::getAppCode, tokenData.getAppCode());
} else {
qw.isNull(FlowEntry::getAppCode);
}
if (tokenData.getTenantId() != null) {
qw.eq(FlowEntry::getTenantId, tokenData.getTenantId());
} else {
qw.isNull(FlowEntry::getTenantId);
}
return commonRedisUtil.getFromCacheWithQueryWrapper(key, qw, flowEntryMapper::selectOne, FlowEntry.class);
}
@Override
public List<FlowEntryPublish> getFlowEntryPublishList(Long entryId) {
FlowEntryPublish filter = new FlowEntryPublish();
filter.setEntryId(entryId);
LambdaQueryWrapper<FlowEntryPublish> queryWrapper = new LambdaQueryWrapper<>(filter);
queryWrapper.orderByDesc(FlowEntryPublish::getEntryPublishId);
return flowEntryPublishMapper.selectList(queryWrapper);
}
@Override
public List<FlowEntryPublish> getFlowEntryPublishList(Set<String> processDefinitionIdSet) {
LambdaQueryWrapper<FlowEntryPublish> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(FlowEntryPublish::getProcessDefinitionId, processDefinitionIdSet);
return flowEntryPublishMapper.selectList(queryWrapper);
}
@Override
public FlowEntryPublish getFlowEntryPublishFromCache(Long entryPublishId) {
String key = FlowRedisKeyUtil.makeFlowEntryPublishKey(entryPublishId);
return commonRedisUtil.getFromCache(
key, entryPublishId, flowEntryPublishMapper::selectById, FlowEntryPublish.class, FLOW_ENTRY_PUBLISH_TTL);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updateFlowEntryMainVersion(FlowEntry flowEntry, FlowEntryPublish newMainFlowEntryPublish) {
commonRedisUtil.evictFormCache(
FlowRedisKeyUtil.makeFlowEntryKey(flowEntry.getProcessDefinitionKey()));
commonRedisUtil.evictFormCache(
FlowRedisKeyUtil.makeFlowEntryPublishKey(newMainFlowEntryPublish.getEntryPublishId()));
FlowEntryPublish oldMainFlowEntryPublish =
flowEntryPublishMapper.selectById(flowEntry.getMainEntryPublishId());
if (oldMainFlowEntryPublish != null) {
commonRedisUtil.evictFormCache(
FlowRedisKeyUtil.makeFlowEntryPublishKey(oldMainFlowEntryPublish.getEntryPublishId()));
oldMainFlowEntryPublish.setMainVersion(false);
flowEntryPublishMapper.updateById(oldMainFlowEntryPublish);
}
newMainFlowEntryPublish.setMainVersion(true);
flowEntryPublishMapper.updateById(newMainFlowEntryPublish);
FlowEntry updatedEntry = new FlowEntry();
updatedEntry.setEntryId(flowEntry.getEntryId());
updatedEntry.setMainEntryPublishId(newMainFlowEntryPublish.getEntryPublishId());
flowEntryMapper.updateById(updatedEntry);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void suspendFlowEntryPublish(FlowEntryPublish flowEntryPublish) {
commonRedisUtil.evictFormCache(
FlowRedisKeyUtil.makeFlowEntryPublishKey(flowEntryPublish.getEntryPublishId()));
FlowEntryPublish updatedEntryPublish = new FlowEntryPublish();
updatedEntryPublish.setEntryPublishId(flowEntryPublish.getEntryPublishId());
updatedEntryPublish.setActiveStatus(false);
flowEntryPublishMapper.updateById(updatedEntryPublish);
flowApiService.suspendProcessDefinition(flowEntryPublish.getProcessDefinitionId());
}
@Transactional(rollbackFor = Exception.class)
@Override
public void activateFlowEntryPublish(FlowEntryPublish flowEntryPublish) {
commonRedisUtil.evictFormCache(
FlowRedisKeyUtil.makeFlowEntryPublishKey(flowEntryPublish.getEntryPublishId()));
FlowEntryPublish updatedEntryPublish = new FlowEntryPublish();
updatedEntryPublish.setEntryPublishId(flowEntryPublish.getEntryPublishId());
updatedEntryPublish.setActiveStatus(true);
flowEntryPublishMapper.updateById(updatedEntryPublish);
flowApiService.activateProcessDefinition(flowEntryPublish.getProcessDefinitionId());
}
@Override
public boolean existByProcessDefinitionKey(String processDefinitionKey) {
FlowEntry filter = new FlowEntry();
filter.setProcessDefinitionKey(processDefinitionKey);
return CollUtil.isNotEmpty(this.getFlowEntryList(filter, null));
}
@Override
public CallResult verifyRelatedData(FlowEntry flowEntry, FlowEntry originalFlowEntry) {
String errorMessageFormat = "数据验证失败,关联的%s并不存在请刷新后重试";
if (this.needToVerify(flowEntry, originalFlowEntry, FlowEntry::getCategoryId)
&& !flowCategoryService.existId(flowEntry.getCategoryId())) {
return CallResult.error(String.format(errorMessageFormat, "流程类别Id"));
}
return CallResult.ok();
}
private void insertBuiltinEntryVariables(Long entryId) {
Date now = new Date();
FlowEntryVariable operationTypeVariable = new FlowEntryVariable();
operationTypeVariable.setVariableId(idGenerator.nextLongId());
operationTypeVariable.setEntryId(entryId);
operationTypeVariable.setVariableName(FlowConstant.OPERATION_TYPE_VAR);
operationTypeVariable.setShowName("审批类型");
operationTypeVariable.setVariableType(FlowVariableType.TASK);
operationTypeVariable.setBuiltin(true);
operationTypeVariable.setCreateTime(now);
flowEntryVariableService.saveNew(operationTypeVariable);
FlowEntryVariable startUserNameVariable = new FlowEntryVariable();
startUserNameVariable.setVariableId(idGenerator.nextLongId());
startUserNameVariable.setEntryId(entryId);
startUserNameVariable.setVariableName("startUserName");
startUserNameVariable.setShowName("流程启动用户");
startUserNameVariable.setVariableType(FlowVariableType.INSTANCE);
startUserNameVariable.setBuiltin(true);
startUserNameVariable.setCreateTime(now);
flowEntryVariableService.saveNew(startUserNameVariable);
}
private void insertEntryPublishVariables(List<FlowEntryVariable> entryVariableList, Long entryPublishId) {
if (CollUtil.isEmpty(entryVariableList)) {
return;
}
List<FlowEntryPublishVariable> entryPublishVariableList =
MyModelUtil.copyCollectionTo(entryVariableList, FlowEntryPublishVariable.class);
for (FlowEntryPublishVariable variable : entryPublishVariableList) {
variable.setVariableId(idGenerator.nextLongId());
variable.setEntryPublishId(entryPublishId);
}
flowEntryPublishVariableMapper.insertList(entryPublishVariableList);
}
private void mergeTaskNotifyData(FlowEntryExtensionData flowEntryExtensionData, List<FlowTaskExt> flowTaskExtList) {
if (CollUtil.isEmpty(flowEntryExtensionData.getNotifyTypes())) {
return;
}
List<String> flowTaskNotifyTypes =
flowEntryExtensionData.getNotifyTypes().stream().filter(StrUtil::isNotBlank).collect(Collectors.toList());
if (CollUtil.isEmpty(flowTaskNotifyTypes)) {
return;
}
for (FlowTaskExt flowTaskExt : flowTaskExtList) {
if (flowTaskExt.getExtraDataJson() == null) {
JSONObject o = new JSONObject();
o.put(FlowConstant.USER_TASK_NOTIFY_TYPES_KEY, flowTaskNotifyTypes);
flowTaskExt.setExtraDataJson(o.toJSONString());
} else {
FlowUserTaskExtData taskExtData =
JSON.parseObject(flowTaskExt.getExtraDataJson(), FlowUserTaskExtData.class);
if (CollUtil.isEmpty(taskExtData.getFlowNotifyTypeList())) {
taskExtData.setFlowNotifyTypeList(flowTaskNotifyTypes);
} else {
Set<String> notifyTypesSet = taskExtData.getFlowNotifyTypeList()
.stream().filter(StrUtil::isNotBlank).collect(Collectors.toSet());
notifyTypesSet.addAll(flowTaskNotifyTypes);
taskExtData.setFlowNotifyTypeList(new LinkedList<>(notifyTypesSet));
}
flowTaskExt.setExtraDataJson(JSON.toJSONString(taskExtData));
}
}
}
private void doAddLatestApprovalStatusListener(Collection<FlowElement> elementList) {
List<FlowElement> sequenceFlowList =
elementList.stream().filter(SequenceFlow.class::isInstance).toList();
for (FlowElement sequenceFlow : sequenceFlowList) {
FlowElementExtProperty extProperty = flowTaskExtService.buildFlowElementExt(sequenceFlow);
if (extProperty != null && extProperty.getLatestApprovalStatus() != null) {
List<FieldExtension> fieldExtensions = new LinkedList<>();
FieldExtension fieldExtension = new FieldExtension();
fieldExtension.setFieldName(FlowConstant.LATEST_APPROVAL_STATUS_KEY);
fieldExtension.setStringValue(extProperty.getLatestApprovalStatus().toString());
fieldExtensions.add(fieldExtension);
flowApiService.addExecutionListener(
sequenceFlow, UpdateLatestApprovalStatusListener.class, "start", fieldExtensions);
}
}
List<SubProcess> subProcesseList = elementList.stream()
.filter(SubProcess.class::isInstance).map(SubProcess.class::cast).toList();
for (SubProcess subProcess : subProcesseList) {
this.doAddLatestApprovalStatusListener(subProcess.getFlowElements());
}
}
private void calculateAllElementList(Collection<FlowElement> elements, List<FlowElement> resultList) {
resultList.addAll(elements);
for (FlowElement element : elements) {
if (element instanceof SubProcess) {
this.calculateAllElementList(((SubProcess) element).getFlowElements(), resultList);
}
}
}
private void processFlowTaskExtList(List<FlowTaskExt> flowTaskExtList, BpmnModel bpmnModel) {
List<FlowElement> elementList = new LinkedList<>();
this.calculateAllElementList(bpmnModel.getMainProcess().getFlowElements(), elementList);
this.doAddLatestApprovalStatusListener(elementList);
Map<String, FlowElement> elementMap = elementList.stream()
.filter(UserTask.class::isInstance).collect(Collectors.toMap(FlowElement::getId, c -> c));
BaseFlowIdentityExtHelper flowIdentityExtHelper = flowCustomExtFactory.getFlowIdentityExtHelper();
for (FlowTaskExt t : flowTaskExtList) {
UserTask userTask = (UserTask) elementMap.get(t.getTaskId());
flowApiService.addTaskCreateListener(userTask, FlowUserTaskListener.class);
Map<String, List<ExtensionAttribute>> attributes = userTask.getAttributes();
if (CollUtil.isNotEmpty(attributes.get(FlowConstant.USER_TASK_AUTO_SKIP_KEY))) {
flowApiService.addTaskCreateListener(userTask, AutoSkipTaskListener.class);
}
// 如果流程图中包含部门领导审批和上级部门领导审批的选项,就需要注册 FlowCustomExtFactory 工厂中的
// BaseFlowIdentityExtHelper 对象,该注册操作需要业务模块中实现。
if (StrUtil.equals(t.getGroupType(), FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER)) {
userTask.setCandidateGroups(
CollUtil.newArrayList("${" + FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER_VAR + "}"));
Assert.notNull(flowIdentityExtHelper);
flowApiService.addTaskCreateListener(userTask, flowIdentityExtHelper.getUpDeptPostLeaderListener());
} else if (StrUtil.equals(t.getGroupType(), FlowConstant.GROUP_TYPE_DEPT_POST_LEADER)) {
userTask.setCandidateGroups(
CollUtil.newArrayList("${" + FlowConstant.GROUP_TYPE_DEPT_POST_LEADER_VAR + "}"));
Assert.notNull(flowIdentityExtHelper);
flowApiService.addTaskCreateListener(userTask, flowIdentityExtHelper.getDeptPostLeaderListener());
} else if (StrUtil.equals(t.getGroupType(), FlowConstant.GROUP_TYPE_POST)) {
Assert.notNull(t.getDeptPostListJson());
List<FlowTaskPostCandidateGroup> groupDataList =
JSON.parseArray(t.getDeptPostListJson(), FlowTaskPostCandidateGroup.class);
List<String> candidateGroupList =
FlowTaskPostCandidateGroup.buildCandidateGroupList(groupDataList);
userTask.setCandidateGroups(candidateGroupList);
}
this.processFlowTaskExtListener(userTask, t);
}
}
private void processFlowTaskExtListener(UserTask userTask, FlowTaskExt taskExt) {
if (StrUtil.isBlank(taskExt.getExtraDataJson())) {
return;
}
FlowUserTaskExtData userTaskExtData =
JSON.parseObject(taskExt.getExtraDataJson(), FlowUserTaskExtData.class);
if (CollUtil.isNotEmpty(userTaskExtData.getFlowNotifyTypeList())) {
flowApiService.addTaskCreateListener(userTask, FlowTaskNotifyListener.class);
}
}
}

View File

@@ -0,0 +1,137 @@
package com.orangeforms.common.flow.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.orangeforms.common.flow.service.*;
import com.orangeforms.common.flow.dao.*;
import com.orangeforms.common.flow.model.*;
import com.orangeforms.common.core.annotation.MyDataSourceResolver;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.core.base.service.BaseService;
import com.orangeforms.common.core.constant.ApplicationConstant;
import com.orangeforms.common.core.object.MyRelationParam;
import com.orangeforms.common.core.util.DefaultDataSourceResolver;
import com.orangeforms.common.sequence.wrapper.IdGeneratorWrapper;
import com.github.pagehelper.Page;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
/**
* 流程变量数据操作服务类。
*
* @author Jerry
* @date 2024-07-02
*/
@Slf4j
@MyDataSourceResolver(
resolver = DefaultDataSourceResolver.class,
intArg = ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE)
@Service("flowEntryVariableService")
public class FlowEntryVariableServiceImpl extends BaseService<FlowEntryVariable, Long> implements FlowEntryVariableService {
@Autowired
private FlowEntryVariableMapper flowEntryVariableMapper;
@Autowired
private IdGeneratorWrapper idGenerator;
/**
* 返回当前Service的主表Mapper对象。
*
* @return 主表Mapper对象。
*/
@Override
protected BaseDaoMapper<FlowEntryVariable> mapper() {
return flowEntryVariableMapper;
}
/**
* 保存新增对象。
*
* @param flowEntryVariable 新增对象。
* @return 返回新增对象。
*/
@Transactional(rollbackFor = Exception.class)
@Override
public FlowEntryVariable saveNew(FlowEntryVariable flowEntryVariable) {
flowEntryVariable.setVariableId(idGenerator.nextLongId());
flowEntryVariable.setCreateTime(new Date());
flowEntryVariableMapper.insert(flowEntryVariable);
return flowEntryVariable;
}
/**
* 更新数据对象。
*
* @param flowEntryVariable 更新的对象。
* @param originalFlowEntryVariable 原有数据对象。
* @return 成功返回true否则false。
*/
@Transactional(rollbackFor = Exception.class)
@Override
public boolean update(FlowEntryVariable flowEntryVariable, FlowEntryVariable originalFlowEntryVariable) {
flowEntryVariable.setCreateTime(originalFlowEntryVariable.getCreateTime());
// 这里重点提示,在执行主表数据更新之前,如果有哪些字段不支持修改操作,请用原有数据对象字段替换当前数据字段。
UpdateWrapper<FlowEntryVariable> uw = this.createUpdateQueryForNullValue(flowEntryVariable, flowEntryVariable.getVariableId());
return flowEntryVariableMapper.update(flowEntryVariable, uw) == 1;
}
/**
* 删除指定数据。
*
* @param variableId 主键Id。
* @return 成功返回true否则false。
*/
@Transactional(rollbackFor = Exception.class)
@Override
public boolean remove(Long variableId) {
return flowEntryVariableMapper.deleteById(variableId) == 1;
}
/**
* 删除指定流程Id的所有变量。
*
* @param entryId 流程Id。
*/
@Transactional(rollbackFor = Exception.class)
@Override
public void removeByEntryId(Long entryId) {
flowEntryVariableMapper.delete(
new LambdaQueryWrapper<FlowEntryVariable>().eq(FlowEntryVariable::getEntryId, entryId));
}
/**
* 获取单表查询结果。由于没有关联数据查询,因此在仅仅获取单表数据的场景下,效率更高。
* 如果需要同时获取关联数据,请移步(getFlowEntryVariableListWithRelation)方法。
*
* @param filter 过滤对象。
* @param orderBy 排序参数。
* @return 查询结果集。
*/
@Override
public List<FlowEntryVariable> getFlowEntryVariableList(FlowEntryVariable filter, String orderBy) {
return flowEntryVariableMapper.getFlowEntryVariableList(filter, orderBy);
}
/**
* 获取主表的查询结果,以及主表关联的字典数据和一对一从表数据,以及一对一从表的字典数据。
* 该查询会涉及到一对一从表的关联过滤,或一对多从表的嵌套关联过滤,因此性能不如单表过滤。
* 如果仅仅需要获取主表数据,请移步(getFlowEntryVariableList),以便获取更好的查询性能。
*
* @param filter 主表过滤对象。
* @param orderBy 排序参数。
* @return 查询结果集。
*/
@Override
public List<FlowEntryVariable> getFlowEntryVariableListWithRelation(FlowEntryVariable filter, String orderBy) {
List<FlowEntryVariable> resultList = flowEntryVariableMapper.getFlowEntryVariableList(filter, orderBy);
// 在缺省生成的代码中如果查询结果resultList不是Page对象说明没有分页那么就很可能是数据导出接口调用了当前方法。
// 为了避免一次性的大量数据关联,规避因此而造成的系统运行性能冲击,这里手动进行了分批次读取,开发者可按需修改该值。
int batchSize = resultList instanceof Page ? 0 : 1000;
this.buildRelationForDataList(resultList, MyRelationParam.normal(), batchSize);
return resultList;
}
}

View File

@@ -0,0 +1,385 @@
package com.orangeforms.common.flow.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.BooleanUtil;
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.annotation.MyDataSourceResolver;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.core.base.service.BaseService;
import com.orangeforms.common.core.constant.ApplicationConstant;
import com.orangeforms.common.flow.constant.FlowConstant;
import com.orangeforms.common.core.object.TokenData;
import com.orangeforms.common.core.util.DefaultDataSourceResolver;
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.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.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;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
/**
* 工作流消息数据操作服务接口。
*
* @author Jerry
* @date 2024-07-02
*/
@Slf4j
@MyDataSourceResolver(
resolver = DefaultDataSourceResolver.class,
intArg = ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE)
@Service("flowMessageService")
public class FlowMessageServiceImpl extends BaseService<FlowMessage, Long> implements FlowMessageService {
@Autowired
private FlowMessageMapper flowMessageMapper;
@Autowired
private FlowMessageCandidateIdentityMapper flowMessageCandidateIdentityMapper;
@Autowired
private FlowMessageIdentityOperationMapper flowMessageIdentityOperationMapper;
@Autowired
private FlowTaskExtService flowTaskExtService;
@Autowired
private FlowApiService flowApiService;
@Autowired
private FlowCustomExtFactory flowCustomExtFactory;
@Autowired
private IdGeneratorWrapper idGenerator;
/**
* 返回当前Service的主表Mapper对象。
*
* @return 主表Mapper对象。
*/
@Override
protected BaseDaoMapper<FlowMessage> mapper() {
return flowMessageMapper;
}
@Transactional(rollbackFor = Exception.class)
@Override
public FlowMessage saveNew(FlowMessage flowMessage) {
flowMessage.setMessageId(idGenerator.nextLongId());
TokenData tokenData = TokenData.takeFromRequest();
if (tokenData != null) {
flowMessage.setTenantId(tokenData.getTenantId());
flowMessage.setAppCode(tokenData.getAppCode());
flowMessage.setCreateUserId(tokenData.getUserId());
flowMessage.setCreateUsername(tokenData.getShowName());
flowMessage.setUpdateUserId(tokenData.getUserId());
}
flowMessage.setCreateTime(new Date());
flowMessage.setUpdateTime(flowMessage.getCreateTime());
flowMessageMapper.insert(flowMessage);
return flowMessage;
}
@Transactional(rollbackFor = Exception.class)
@Override
public void saveNewRemindMessage(FlowWorkOrder flowWorkOrder) {
List<Task> taskList =
flowApiService.getProcessInstanceActiveTaskList(flowWorkOrder.getProcessInstanceId());
for (Task task : taskList) {
FlowMessage filter = new FlowMessage();
filter.setTaskId(task.getId());
List<FlowMessage> messageList = flowMessageMapper.selectList(new QueryWrapper<>(filter));
// 同一个任务只能催办一次,多次催办则累加催办次数。
if (CollUtil.isNotEmpty(messageList)) {
for (FlowMessage flowMessage : messageList) {
flowMessage.setRemindCount(flowMessage.getRemindCount() + 1);
flowMessageMapper.updateById(flowMessage);
}
continue;
}
FlowMessage flowMessage = BeanUtil.copyProperties(flowWorkOrder, FlowMessage.class);
flowMessage.setMessageType(FlowMessageType.REMIND_TYPE);
flowMessage.setRemindCount(1);
flowMessage.setProcessInstanceInitiator(flowWorkOrder.getSubmitUsername());
flowMessage.setTaskId(task.getId());
flowMessage.setTaskName(task.getName());
flowMessage.setTaskStartTime(task.getCreateTime());
flowMessage.setTaskAssignee(task.getAssignee());
flowMessage.setTaskFinished(false);
if (TokenData.takeFromRequest() == null) {
Set<String> usernameSet = CollUtil.newHashSet(flowWorkOrder.getSubmitUsername());
Map<String, String> m = flowCustomExtFactory.getFlowIdentityExtHelper().mapUserShowNameByLoginName(usernameSet);
flowMessage.setCreateUsername(m.containsKey(flowWorkOrder.getSubmitUsername())
? m.get(flowWorkOrder.getSubmitUsername()) : flowWorkOrder.getSubmitUsername());
}
this.saveNew(flowMessage);
FlowTaskExt flowTaskExt = flowTaskExtService.getByProcessDefinitionIdAndTaskId(
flowWorkOrder.getProcessDefinitionId(), task.getTaskDefinitionKey());
if (flowTaskExt != null) {
// 插入与当前消息关联任务的候选人
this.saveMessageCandidateIdentityWithMessage(
flowWorkOrder.getProcessInstanceId(), flowTaskExt, task, 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) {
if (copyDataJson.isEmpty()) {
return;
}
ProcessInstance instance = flowApiService.getProcessInstance(task.getProcessInstanceId());
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.setOnlineFormData(true);
// 如果是在线表单这里就保存关联的在线表单Id便于在线表单业务数据的查找。
if (BooleanUtil.isTrue(flowMessage.getOnlineFormData())) {
TaskInfoVo taskInfo = JSON.parseObject(task.getFormKey(), TaskInfoVo.class);
flowMessage.setBusinessDataShot(taskInfo.getFormId().toString());
}
this.saveNew(flowMessage);
for (Map.Entry<String, Object> entry : copyDataJson.entrySet()) {
if (entry.getValue() != null) {
this.saveMessageCandidateIdentityList(
flowMessage.getMessageId(), entry.getKey(), entry.getValue().toString());
}
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updateFinishedStatusByTaskId(String taskId) {
FlowMessage flowMessage = new FlowMessage();
flowMessage.setTaskFinished(true);
LambdaQueryWrapper<FlowMessage> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(FlowMessage::getTaskId, taskId);
flowMessageMapper.update(flowMessage, queryWrapper);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updateFinishedStatusByProcessInstanceId(String processInstanceId) {
FlowMessage flowMessage = new FlowMessage();
flowMessage.setTaskFinished(true);
LambdaQueryWrapper<FlowMessage> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(FlowMessage::getProcessInstanceId, processInstanceId);
flowMessageMapper.update(flowMessage, queryWrapper);
}
@Override
public List<FlowMessage> getRemindingMessageListByUser() {
TokenData tokenData = TokenData.takeFromRequest();
return flowMessageMapper.getRemindingMessageListByUser(
tokenData.getTenantId(), tokenData.getAppCode(), tokenData.getLoginName(), buildGroupIdSet());
}
@Override
public List<FlowMessage> getCopyMessageListByUser(Boolean read) {
TokenData tokenData = TokenData.takeFromRequest();
return flowMessageMapper.getCopyMessageListByUser(
tokenData.getTenantId(), tokenData.getAppCode(), tokenData.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() {
TokenData tokenData = TokenData.takeFromRequest();
return flowMessageMapper.countRemindingMessageListByUser(
tokenData.getTenantId(), tokenData.getAppCode(), tokenData.getLoginName(), buildGroupIdSet());
}
@Override
public int countCopyMessageByUser() {
TokenData tokenData = TokenData.takeFromRequest();
return flowMessageMapper.countCopyMessageListByUser(
tokenData.getTenantId(), tokenData.getAppCode(), tokenData.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());
this.parseAndAddIdArray(groupIdSet, tokenData.getRoleIds());
this.parseAndAddIdArray(groupIdSet, tokenData.getDeptPostIds());
this.parseAndAddIdArray(groupIdSet, tokenData.getPostIds());
if (tokenData.getDeptId() != null) {
groupIdSet.add(tokenData.getDeptId().toString());
}
return groupIdSet;
}
private void parseAndAddIdArray(Set<String> groupIdSet, String idArray) {
if (StrUtil.isNotBlank(idArray)) {
if (groupIdSet == null) {
groupIdSet = new HashSet<>();
}
groupIdSet.addAll(StrUtil.split(idArray, ','));
}
}
private void saveMessageCandidateIdentityWithMessage(
String processInstanceId, FlowTaskExt flowTaskExt, Task task, Long messageId) {
List<String> candidates = flowApiService.getCandidateUsernames(flowTaskExt, task.getId());
if (CollUtil.isNotEmpty(candidates)) {
this.saveMessageCandidateIdentityList(
messageId, FlowConstant.GROUP_TYPE_USER_VAR, CollUtil.join(candidates, ","));
}
this.saveMessageCandidateIdentityList(
messageId, FlowConstant.GROUP_TYPE_ROLE_VAR, flowTaskExt.getRoleIds());
this.saveMessageCandidateIdentityList(
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);
if (v != null) {
this.saveMessageCandidateIdentity(
messageId, FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER_VAR, v.toString());
}
} else if (StrUtil.equals(flowTaskExt.getGroupType(), FlowConstant.GROUP_TYPE_DEPT_POST_LEADER)) {
Object v = flowApiService.getProcessInstanceVariable(
processInstanceId, FlowConstant.GROUP_TYPE_DEPT_POST_LEADER_VAR);
if (v != null) {
this.saveMessageCandidateIdentity(
messageId, FlowConstant.GROUP_TYPE_DEPT_POST_LEADER_VAR, v.toString());
}
} else if (StrUtil.equals(flowTaskExt.getGroupType(), FlowConstant.GROUP_TYPE_POST)) {
Assert.notBlank(flowTaskExt.getDeptPostListJson());
List<FlowTaskPostCandidateGroup> groupDataList =
JSONArray.parseArray(flowTaskExt.getDeptPostListJson(), FlowTaskPostCandidateGroup.class);
for (FlowTaskPostCandidateGroup groupData : groupDataList) {
this.saveMessageCandidateIdentity(messageId, processInstanceId, groupData);
}
}
}
private void saveMessageCandidateIdentity(
Long messageId, String processInstanceId, FlowTaskPostCandidateGroup groupData) {
FlowMessageCandidateIdentity candidateIdentity = new FlowMessageCandidateIdentity();
candidateIdentity.setId(idGenerator.nextLongId());
candidateIdentity.setMessageId(messageId);
candidateIdentity.setCandidateType(groupData.getType());
switch (groupData.getType()) {
case FlowConstant.GROUP_TYPE_ALL_DEPT_POST_VAR:
candidateIdentity.setCandidateId(groupData.getPostId());
flowMessageCandidateIdentityMapper.insert(candidateIdentity);
break;
case FlowConstant.GROUP_TYPE_DEPT_POST_VAR:
candidateIdentity.setCandidateId(groupData.getDeptPostId());
flowMessageCandidateIdentityMapper.insert(candidateIdentity);
break;
case FlowConstant.GROUP_TYPE_SELF_DEPT_POST_VAR:
Object v = flowApiService.getProcessInstanceVariable(
processInstanceId, FlowConstant.SELF_DEPT_POST_PREFIX + groupData.getPostId());
if (v != null) {
candidateIdentity.setCandidateId(v.toString());
flowMessageCandidateIdentityMapper.insert(candidateIdentity);
}
break;
case FlowConstant.GROUP_TYPE_UP_DEPT_POST_VAR:
Object v2 = flowApiService.getProcessInstanceVariable(
processInstanceId, FlowConstant.UP_DEPT_POST_PREFIX + groupData.getPostId());
if (v2 != null) {
candidateIdentity.setCandidateId(v2.toString());
flowMessageCandidateIdentityMapper.insert(candidateIdentity);
}
break;
case FlowConstant.GROUP_TYPE_SIBLING_DEPT_POST_VAR:
Object v3 = flowApiService.getProcessInstanceVariable(
processInstanceId, FlowConstant.SIBLING_DEPT_POST_PREFIX + groupData.getPostId());
if (v3 != null) {
List<String> candidateIds = StrUtil.split(v3.toString(), ",");
for (String candidateId : candidateIds) {
candidateIdentity.setId(idGenerator.nextLongId());
candidateIdentity.setCandidateId(candidateId);
flowMessageCandidateIdentityMapper.insert(candidateIdentity);
}
}
break;
default:
break;
}
}
private void saveMessageCandidateIdentity(Long messageId, String candidateType, String candidateId) {
FlowMessageCandidateIdentity candidateIdentity = new FlowMessageCandidateIdentity();
candidateIdentity.setId(idGenerator.nextLongId());
candidateIdentity.setMessageId(messageId);
candidateIdentity.setCandidateType(candidateType);
candidateIdentity.setCandidateId(candidateId);
flowMessageCandidateIdentityMapper.insert(candidateIdentity);
}
private void saveMessageCandidateIdentityList(Long messageId, String candidateType, String identityIds) {
if (StrUtil.isNotBlank(identityIds)) {
for (String identityId : StrUtil.split(identityIds, ',')) {
FlowMessageCandidateIdentity candidateIdentity = new FlowMessageCandidateIdentity();
candidateIdentity.setId(idGenerator.nextLongId());
candidateIdentity.setMessageId(messageId);
candidateIdentity.setCandidateType(candidateType);
candidateIdentity.setCandidateId(identityId);
flowMessageCandidateIdentityMapper.insert(candidateIdentity);
}
}
}
}

View File

@@ -0,0 +1,87 @@
package com.orangeforms.common.flow.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orangeforms.common.core.annotation.MyDataSourceResolver;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.core.base.service.BaseService;
import com.orangeforms.common.core.constant.ApplicationConstant;
import com.orangeforms.common.core.object.TokenData;
import com.orangeforms.common.core.util.DefaultDataSourceResolver;
import com.orangeforms.common.flow.dao.FlowMultiInstanceTransMapper;
import com.orangeforms.common.flow.model.FlowMultiInstanceTrans;
import com.orangeforms.common.flow.service.FlowMultiInstanceTransService;
import com.orangeforms.common.sequence.wrapper.IdGeneratorWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
/**
* 会签任务操作流水数据操作服务接口。
*
* @author Jerry
* @date 2024-07-02
*/
@Slf4j
@MyDataSourceResolver(
resolver = DefaultDataSourceResolver.class,
intArg = ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE)
@Service("flowMultiInstanceTransService")
public class FlowMultiInstanceTransServiceImpl
extends BaseService<FlowMultiInstanceTrans, Long> implements FlowMultiInstanceTransService {
@Autowired
private FlowMultiInstanceTransMapper flowMultiInstanceTransMapper;
@Autowired
private IdGeneratorWrapper idGenerator;
/**
* 返回当前Service的主表Mapper对象。
*
* @return 主表Mapper对象。
*/
@Override
protected BaseDaoMapper<FlowMultiInstanceTrans> mapper() {
return flowMultiInstanceTransMapper;
}
/**
* 保存新增对象。
*
* @param flowMultiInstanceTrans 新增对象。
* @return 返回新增对象。
*/
@Transactional(rollbackFor = Exception.class)
@Override
public FlowMultiInstanceTrans saveNew(FlowMultiInstanceTrans flowMultiInstanceTrans) {
flowMultiInstanceTrans.setId(idGenerator.nextLongId());
TokenData tokenData = TokenData.takeFromRequest();
flowMultiInstanceTrans.setCreateUserId(tokenData.getUserId());
flowMultiInstanceTrans.setCreateLoginName(tokenData.getLoginName());
flowMultiInstanceTrans.setCreateUsername(tokenData.getShowName());
flowMultiInstanceTrans.setCreateTime(new Date());
flowMultiInstanceTransMapper.insert(flowMultiInstanceTrans);
return flowMultiInstanceTrans;
}
@Override
public FlowMultiInstanceTrans getByExecutionId(String executionId, String taskId) {
LambdaQueryWrapper<FlowMultiInstanceTrans> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(FlowMultiInstanceTrans::getExecutionId, executionId);
queryWrapper.eq(FlowMultiInstanceTrans::getTaskId, taskId);
return flowMultiInstanceTransMapper.selectOne(queryWrapper);
}
@Override
public FlowMultiInstanceTrans getWithAssigneeListByMultiInstanceExecId(String multiInstanceExecId) {
if (multiInstanceExecId == null) {
return null;
}
LambdaQueryWrapper<FlowMultiInstanceTrans> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(FlowMultiInstanceTrans::getMultiInstanceExecId, multiInstanceExecId);
queryWrapper.isNotNull(FlowMultiInstanceTrans::getAssigneeList);
return flowMultiInstanceTransMapper.selectOne(queryWrapper);
}
}

View File

@@ -0,0 +1,142 @@
package com.orangeforms.common.flow.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.orangeforms.common.flow.service.*;
import com.orangeforms.common.flow.dao.*;
import com.orangeforms.common.flow.model.*;
import com.orangeforms.common.core.annotation.MyDataSourceResolver;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.core.base.service.BaseService;
import com.orangeforms.common.core.constant.ApplicationConstant;
import com.orangeforms.common.core.object.TokenData;
import com.orangeforms.common.core.util.DefaultDataSourceResolver;
import com.orangeforms.common.sequence.wrapper.IdGeneratorWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
/**
* 流程任务批注数据操作服务类。
*
* @author Jerry
* @date 2024-07-02
*/
@Slf4j
@MyDataSourceResolver(
resolver = DefaultDataSourceResolver.class,
intArg = ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE)
@Service("flowTaskCommentService")
public class FlowTaskCommentServiceImpl extends BaseService<FlowTaskComment, Long> implements FlowTaskCommentService {
@Autowired
private FlowTaskCommentMapper flowTaskCommentMapper;
@Autowired
private IdGeneratorWrapper idGenerator;
/**
* 返回当前Service的主表Mapper对象。
*
* @return 主表Mapper对象。
*/
@Override
protected BaseDaoMapper<FlowTaskComment> mapper() {
return flowTaskCommentMapper;
}
/**
* 保存新增对象。
*
* @param flowTaskComment 新增对象。
* @return 返回新增对象。
*/
@Transactional(rollbackFor = Exception.class)
@Override
public FlowTaskComment saveNew(FlowTaskComment flowTaskComment) {
flowTaskComment.setId(idGenerator.nextLongId());
TokenData tokenData = TokenData.takeFromRequest();
if (tokenData != null) {
flowTaskComment.setHeadImageUrl(tokenData.getHeadImageUrl());
flowTaskComment.setCreateUserId(tokenData.getUserId());
flowTaskComment.setCreateLoginName(tokenData.getLoginName());
flowTaskComment.setCreateUsername(tokenData.getShowName());
}
flowTaskComment.setCreateTime(new Date());
flowTaskCommentMapper.insert(flowTaskComment);
FlowTaskComment.setToRequest(flowTaskComment);
return flowTaskComment;
}
/**
* 查询指定流程实例Id下的所有审批任务的批注。
*
* @param processInstanceId 流程实例Id。
* @return 查询结果集。
*/
@Override
public List<FlowTaskComment> getFlowTaskCommentList(String processInstanceId) {
LambdaQueryWrapper<FlowTaskComment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(FlowTaskComment::getProcessInstanceId, processInstanceId);
queryWrapper.orderByAsc(FlowTaskComment::getId);
return flowTaskCommentMapper.selectList(queryWrapper);
}
@Override
public List<FlowTaskComment> getFlowTaskCommentListByTaskIds(Set<String> taskIdSet) {
LambdaQueryWrapper<FlowTaskComment> queryWrapper =
new LambdaQueryWrapper<FlowTaskComment>().in(FlowTaskComment::getTaskId, taskIdSet);
queryWrapper.orderByDesc(FlowTaskComment::getId);
return flowTaskCommentMapper.selectList(queryWrapper);
}
@Override
public FlowTaskComment getLatestFlowTaskComment(String 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);
}
@Override
public List<FlowTaskComment> getFlowTaskCommentListByExecutionId(
String processInstanceId, String taskId, String executionId) {
LambdaQueryWrapper<FlowTaskComment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(FlowTaskComment::getProcessInstanceId, processInstanceId);
queryWrapper.eq(FlowTaskComment::getTaskId, taskId);
queryWrapper.eq(FlowTaskComment::getExecutionId, executionId);
queryWrapper.orderByAsc(FlowTaskComment::getCreateTime);
return flowTaskCommentMapper.selectList(queryWrapper);
}
@Override
public List<FlowTaskComment> getFlowTaskCommentListByMultiInstanceExecId(String multiInstanceExecId) {
LambdaQueryWrapper<FlowTaskComment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(FlowTaskComment::getMultiInstanceExecId, multiInstanceExecId);
return flowTaskCommentMapper.selectList(queryWrapper);
}
}

View File

@@ -0,0 +1,622 @@
package com.orangeforms.common.flow.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
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.QueryWrapper;
import com.orangeforms.common.core.annotation.MyDataSourceResolver;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.core.base.service.BaseService;
import com.orangeforms.common.core.constant.ApplicationConstant;
import com.orangeforms.common.core.exception.MyRuntimeException;
import com.orangeforms.common.core.object.Tuple2;
import com.orangeforms.common.core.util.DefaultDataSourceResolver;
import com.orangeforms.common.flow.constant.FlowApprovalType;
import com.orangeforms.common.flow.constant.FlowConstant;
import com.orangeforms.common.flow.object.FlowElementExtProperty;
import com.orangeforms.common.flow.object.FlowTaskMultiSignAssign;
import com.orangeforms.common.flow.object.FlowUserTaskExtData;
import com.orangeforms.common.flow.service.*;
import com.orangeforms.common.flow.dao.*;
import com.orangeforms.common.flow.model.*;
import com.orangeforms.common.flow.util.BaseFlowIdentityExtHelper;
import com.orangeforms.common.flow.util.FlowCustomExtFactory;
import com.orangeforms.common.flow.vo.FlowUserInfoVo;
import com.orangeforms.common.flow.vo.TaskInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.task.api.TaskInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
@Slf4j
@MyDataSourceResolver(
resolver = DefaultDataSourceResolver.class,
intArg = ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE)
@Service("flowTaskExtService")
public class FlowTaskExtServiceImpl extends BaseService<FlowTaskExt, String> implements FlowTaskExtService {
@Autowired
private FlowTaskExtMapper flowTaskExtMapper;
@Autowired
private FlowEntryVariableService flowEntryVariableService;
@Autowired
private FlowCustomExtFactory flowCustomExtFactory;
@Autowired
private FlowApiService flowApiService;
@Autowired
private FlowMultiInstanceTransService flowMultiInstanceTransService;
@Autowired
private FlowTaskCommentService flowTaskCommentService;
private static final String ID = "id";
private static final String TYPE = "type";
private static final String LABEL = "label";
private static final String NAME = "name";
private static final String VALUE = "value";
/**
* 返回当前Service的主表Mapper对象。
*
* @return 主表Mapper对象。
*/
@Override
protected BaseDaoMapper<FlowTaskExt> mapper() {
return flowTaskExtMapper;
}
@Transactional(rollbackFor = Exception.class)
@Override
public void saveBatch(List<FlowTaskExt> flowTaskExtList) {
if (CollUtil.isNotEmpty(flowTaskExtList)) {
flowTaskExtMapper.insertList(flowTaskExtList);
}
}
@Override
public FlowTaskExt getByProcessDefinitionIdAndTaskId(String processDefinitionId, String taskId) {
FlowTaskExt filter = new FlowTaskExt();
filter.setProcessDefinitionId(processDefinitionId);
filter.setTaskId(taskId);
return flowTaskExtMapper.selectOne(new QueryWrapper<>(filter));
}
@Override
public List<FlowTaskExt> getByProcessDefinitionId(String processDefinitionId) {
FlowTaskExt filter = new FlowTaskExt();
filter.setProcessDefinitionId(processDefinitionId);
return flowTaskExtMapper.selectList(new QueryWrapper<>(filter));
}
@Override
public List<FlowUserInfoVo> getCandidateUserInfoList(
String processInstanceId,
FlowTaskExt flowTaskExt,
TaskInfo taskInfo,
boolean isMultiInstanceTask,
boolean historic) {
List<FlowUserInfoVo> resultUserMapList = new LinkedList<>();
if (!isMultiInstanceTask && this.buildTransferUserList(taskInfo, resultUserMapList)) {
return resultUserMapList;
}
Set<String> loginNameSet = new HashSet<>();
this.buildFlowUserInfoListByDeptAndRoleIds(flowTaskExt, loginNameSet, resultUserMapList);
BaseFlowIdentityExtHelper flowIdentityExtHelper = flowCustomExtFactory.getFlowIdentityExtHelper();
Set<String> usernameSet = new HashSet<>();
switch (flowTaskExt.getGroupType()) {
case FlowConstant.GROUP_TYPE_ASSIGNEE:
usernameSet.add(taskInfo.getAssignee());
break;
case FlowConstant.GROUP_TYPE_DEPT_POST_LEADER:
String deptPostLeaderId = flowApiService.getExecutionVariableStringWithSafe(
taskInfo.getExecutionId(), FlowConstant.GROUP_TYPE_DEPT_POST_LEADER_VAR);
List<FlowUserInfoVo> userInfoList =
flowIdentityExtHelper.getUserInfoListByDeptPostIds(CollUtil.newHashSet(deptPostLeaderId));
this.buildUserMapList(userInfoList, loginNameSet, resultUserMapList);
break;
case FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER:
String upDeptPostLeaderId = flowApiService.getExecutionVariableStringWithSafe(
taskInfo.getExecutionId(), FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER_VAR);
List<FlowUserInfoVo> upUserInfoList =
flowIdentityExtHelper.getUserInfoListByDeptPostIds(CollUtil.newHashSet(upDeptPostLeaderId));
this.buildUserMapList(upUserInfoList, loginNameSet, resultUserMapList);
break;
default:
break;
}
List<String> candidateUsernames = flowApiService.getCandidateUsernames(flowTaskExt, taskInfo.getId());
if (CollUtil.isNotEmpty(candidateUsernames)) {
usernameSet.addAll(candidateUsernames);
}
if (isMultiInstanceTask) {
List<String> assigneeList = this.getAssigneeList(taskInfo.getExecutionId(), taskInfo.getId());
if (CollUtil.isNotEmpty(assigneeList)) {
usernameSet.addAll(assigneeList);
}
}
if (CollUtil.isNotEmpty(usernameSet)) {
List<FlowUserInfoVo> userInfoList = flowIdentityExtHelper.getUserInfoListByUsernameSet(usernameSet);
this.buildUserMapList(userInfoList, loginNameSet, resultUserMapList);
}
Tuple2<Set<String>, Set<String>> tuple2 =
flowApiService.getDeptPostIdAndPostIds(flowTaskExt, processInstanceId, historic);
Set<String> postIdSet = tuple2.getSecond();
Set<String> deptPostIdSet = tuple2.getFirst();
if (CollUtil.isNotEmpty(postIdSet)) {
List<FlowUserInfoVo> userInfoList = flowIdentityExtHelper.getUserInfoListByPostIds(postIdSet);
this.buildUserMapList(userInfoList, loginNameSet, resultUserMapList);
}
if (CollUtil.isNotEmpty(deptPostIdSet)) {
List<FlowUserInfoVo> userInfoList = flowIdentityExtHelper.getUserInfoListByDeptPostIds(deptPostIdSet);
this.buildUserMapList(userInfoList, loginNameSet, resultUserMapList);
}
return resultUserMapList;
}
@Override
public List<FlowUserInfoVo> getCandidateUserInfoList(
String processInstanceId,
String executionId,
FlowTaskExt flowTaskExt) {
List<FlowUserInfoVo> resultUserMapList = new LinkedList<>();
Set<String> loginNameSet = new HashSet<>();
this.buildFlowUserInfoListByDeptAndRoleIds(flowTaskExt, loginNameSet, resultUserMapList);
Set<String> usernameSet = new HashSet<>();
BaseFlowIdentityExtHelper flowIdentityExtHelper = flowCustomExtFactory.getFlowIdentityExtHelper();
switch (flowTaskExt.getGroupType()) {
case FlowConstant.GROUP_TYPE_DEPT_POST_LEADER:
String deptPostLeaderId = flowApiService.getExecutionVariableStringWithSafe(
executionId, FlowConstant.GROUP_TYPE_DEPT_POST_LEADER_VAR);
List<FlowUserInfoVo> userInfoList =
flowIdentityExtHelper.getUserInfoListByDeptPostIds(CollUtil.newHashSet(deptPostLeaderId));
this.buildUserMapList(userInfoList, loginNameSet, resultUserMapList);
break;
case FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER:
String upDeptPostLeaderId = flowApiService.getExecutionVariableStringWithSafe(
executionId, FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER_VAR);
List<FlowUserInfoVo> upUserInfoList =
flowIdentityExtHelper.getUserInfoListByDeptPostIds(CollUtil.newHashSet(upDeptPostLeaderId));
this.buildUserMapList(upUserInfoList, loginNameSet, resultUserMapList);
break;
default:
break;
}
List<String> candidateUsernames;
if (StrUtil.isBlank(flowTaskExt.getCandidateUsernames())) {
candidateUsernames = Collections.emptyList();
} else {
if (!StrUtil.equals(flowTaskExt.getCandidateUsernames(), "${" + FlowConstant.TASK_APPOINTED_ASSIGNEE_VAR + "}")) {
candidateUsernames = StrUtil.split(flowTaskExt.getCandidateUsernames(), ",");
} else {
Object v = flowApiService.getExecutionVariableStringWithSafe(executionId, FlowConstant.TASK_APPOINTED_ASSIGNEE_VAR);
candidateUsernames = v == null ? null : StrUtil.split(v.toString(), ",");
}
}
if (CollUtil.isNotEmpty(candidateUsernames)) {
usernameSet.addAll(candidateUsernames);
}
if (CollUtil.isNotEmpty(usernameSet)) {
List<FlowUserInfoVo> userInfoList = flowIdentityExtHelper.getUserInfoListByUsernameSet(usernameSet);
this.buildUserMapList(userInfoList, loginNameSet, resultUserMapList);
}
Tuple2<Set<String>, Set<String>> tuple2 =
flowApiService.getDeptPostIdAndPostIds(flowTaskExt, processInstanceId, false);
Set<String> postIdSet = tuple2.getSecond();
Set<String> deptPostIdSet = tuple2.getFirst();
if (CollUtil.isNotEmpty(postIdSet)) {
List<FlowUserInfoVo> userInfoList = flowIdentityExtHelper.getUserInfoListByPostIds(postIdSet);
this.buildUserMapList(userInfoList, loginNameSet, resultUserMapList);
}
if (CollUtil.isNotEmpty(deptPostIdSet)) {
List<FlowUserInfoVo> userInfoList = flowIdentityExtHelper.getUserInfoListByDeptPostIds(deptPostIdSet);
this.buildUserMapList(userInfoList, loginNameSet, resultUserMapList);
}
return resultUserMapList;
}
private void buildUserMapList(
List<FlowUserInfoVo> userInfoList, Set<String> loginNameSet, List<FlowUserInfoVo> userMapList) {
if (CollUtil.isEmpty(userInfoList)) {
return;
}
for (FlowUserInfoVo userInfo : userInfoList) {
if (!loginNameSet.contains(userInfo.getLoginName())) {
loginNameSet.add(userInfo.getLoginName());
userMapList.add(userInfo);
}
}
}
@Override
public FlowTaskExt buildTaskExtByUserTask(UserTask userTask) {
FlowTaskExt flowTaskExt = new FlowTaskExt();
flowTaskExt.setTaskId(userTask.getId());
String formKey = userTask.getFormKey();
if (StrUtil.isNotBlank(formKey)) {
TaskInfoVo taskInfoVo = JSON.parseObject(formKey, TaskInfoVo.class);
flowTaskExt.setGroupType(taskInfoVo.getGroupType());
}
JSONObject extraDataJson = this.buildFlowTaskExtensionData(userTask);
if (extraDataJson != null) {
flowTaskExt.setExtraDataJson(extraDataJson.toJSONString());
}
Map<String, List<ExtensionElement>> extensionMap = userTask.getExtensionElements();
if (MapUtil.isEmpty(extensionMap)) {
return flowTaskExt;
}
List<JSONObject> operationList = this.buildOperationListExtensionElement(extensionMap);
if (CollUtil.isNotEmpty(operationList)) {
flowTaskExt.setOperationListJson(JSON.toJSONString(operationList));
}
List<JSONObject> variableList = this.buildVariableListExtensionElement(extensionMap);
if (CollUtil.isNotEmpty(variableList)) {
flowTaskExt.setVariableListJson(JSON.toJSONString(variableList));
}
JSONObject assigneeListObject = this.buildAssigneeListExtensionElement(extensionMap);
if (assigneeListObject != null) {
flowTaskExt.setAssigneeListJson(JSON.toJSONString(assigneeListObject));
}
List<JSONObject> deptPostList = this.buildDeptPostListExtensionElement(extensionMap);
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);
String value = candidateGroupObject.getString(VALUE);
switch (type) {
case "DEPT":
flowTaskExt.setDeptIds(value);
break;
case "ROLE":
flowTaskExt.setRoleIds(value);
break;
case "USERS":
flowTaskExt.setCandidateUsernames(value);
break;
default:
break;
}
}
return flowTaskExt;
}
@Override
public List<FlowTaskExt> buildTaskExtList(BpmnModel bpmnModel) {
List<Process> processList = bpmnModel.getProcesses();
List<FlowTaskExt> flowTaskExtList = new LinkedList<>();
for (Process process : processList) {
for (FlowElement element : process.getFlowElements()) {
this.doBuildTaskExtList(element, flowTaskExtList);
}
}
return flowTaskExtList;
}
@Override
public List<JSONObject> buildOperationListExtensionElement(Map<String, List<ExtensionElement>> extensionMap) {
List<ExtensionElement> formOperationElements =
this.getMyExtensionElementList(extensionMap, "operationList", "formOperation");
if (CollUtil.isEmpty(formOperationElements)) {
return Collections.emptyList();
}
List<JSONObject> resultList = new LinkedList<>();
for (ExtensionElement e : formOperationElements) {
JSONObject operationJsonData = new JSONObject();
operationJsonData.put(ID, e.getAttributeValue(null, ID));
operationJsonData.put(LABEL, e.getAttributeValue(null, LABEL));
operationJsonData.put(TYPE, e.getAttributeValue(null, TYPE));
operationJsonData.put("showOrder", e.getAttributeValue(null, "showOrder"));
operationJsonData.put("latestApprovalStatus", e.getAttributeValue(null, "latestApprovalStatus"));
String multiSignAssignee = e.getAttributeValue(null, "multiSignAssignee");
if (StrUtil.isNotBlank(multiSignAssignee)) {
operationJsonData.put("multiSignAssignee",
JSON.parseObject(multiSignAssignee, FlowTaskMultiSignAssign.class));
}
resultList.add(operationJsonData);
}
return resultList;
}
@Override
public List<JSONObject> buildVariableListExtensionElement(Map<String, List<ExtensionElement>> extensionMap) {
List<ExtensionElement> formVariableElements =
this.getMyExtensionElementList(extensionMap, "variableList", "formVariable");
if (CollUtil.isEmpty(formVariableElements)) {
return Collections.emptyList();
}
Set<Long> variableIdSet = new HashSet<>();
for (ExtensionElement e : formVariableElements) {
String id = e.getAttributeValue(null, ID);
variableIdSet.add(Long.parseLong(id));
}
List<FlowEntryVariable> variableList = flowEntryVariableService.getInList(variableIdSet);
List<JSONObject> resultList = new LinkedList<>();
for (FlowEntryVariable variable : variableList) {
resultList.add((JSONObject) JSON.toJSON(variable));
}
return resultList;
}
@Override
public FlowElementExtProperty buildFlowElementExt(FlowElement element) {
JSONObject propertiesData = this.buildFlowElementExtToJson(element);
return propertiesData == null ? null : propertiesData.toJavaObject(FlowElementExtProperty.class);
}
@Override
public JSONObject buildFlowElementExtToJson(FlowElement element) {
Map<String, List<ExtensionElement>> extensionMap = element.getExtensionElements();
List<ExtensionElement> propertiesElements =
this.getMyExtensionElementList(extensionMap, "properties", "property");
if (CollUtil.isEmpty(propertiesElements)) {
return null;
}
JSONObject propertiesData = new JSONObject();
for (ExtensionElement e : propertiesElements) {
String name = e.getAttributeValue(null, NAME);
String value = e.getAttributeValue(null, VALUE);
propertiesData.put(name, value);
}
return propertiesData;
}
private void doBuildTaskExtList(FlowElement element, List<FlowTaskExt> flowTaskExtList) {
if (element instanceof UserTask) {
FlowTaskExt flowTaskExt = this.buildTaskExtByUserTask((UserTask) element);
flowTaskExtList.add(flowTaskExt);
} else if (element instanceof SubProcess) {
Collection<FlowElement> flowElements = ((SubProcess) element).getFlowElements();
for (FlowElement element1 : flowElements) {
this.doBuildTaskExtList(element1, flowTaskExtList);
}
}
}
private void buildFlowUserInfoListByDeptAndRoleIds(
FlowTaskExt flowTaskExt, Set<String> loginNameSet, List<FlowUserInfoVo> resultUserMapList) {
BaseFlowIdentityExtHelper flowIdentityExtHelper = flowCustomExtFactory.getFlowIdentityExtHelper();
if (StrUtil.isNotBlank(flowTaskExt.getDeptIds())) {
Set<String> deptIdSet = CollUtil.newHashSet(StrUtil.split(flowTaskExt.getDeptIds(), ','));
List<FlowUserInfoVo> userInfoList = flowIdentityExtHelper.getUserInfoListByDeptIds(deptIdSet);
this.buildUserMapList(userInfoList, loginNameSet, resultUserMapList);
}
if (StrUtil.isNotBlank(flowTaskExt.getRoleIds())) {
Set<String> roleIdSet = CollUtil.newHashSet(StrUtil.split(flowTaskExt.getRoleIds(), ','));
List<FlowUserInfoVo> userInfoList = flowIdentityExtHelper.getUserInfoListByRoleIds(roleIdSet);
this.buildUserMapList(userInfoList, loginNameSet, resultUserMapList);
}
}
private void buildFlowTaskTimeoutExtensionData(
Map<String, List<ExtensionAttribute>> attributeMap, JSONObject extraDataJson) {
List<ExtensionAttribute> timeoutHandleWayAttributes = attributeMap.get(FlowConstant.TASK_TIMEOUT_HANDLE_WAY);
if (CollUtil.isNotEmpty(timeoutHandleWayAttributes)) {
String handleWay = timeoutHandleWayAttributes.get(0).getValue();
extraDataJson.put(FlowConstant.TASK_TIMEOUT_HANDLE_WAY, handleWay);
List<ExtensionAttribute> timeoutHoursAttributes = attributeMap.get(FlowConstant.TASK_TIMEOUT_HOURS);
if (CollUtil.isEmpty(timeoutHoursAttributes)) {
throw new MyRuntimeException("没有设置任务超时小时数!");
}
Integer timeoutHours = Integer.valueOf(timeoutHoursAttributes.get(0).getValue());
extraDataJson.put(FlowConstant.TASK_TIMEOUT_HOURS, timeoutHours);
if (StrUtil.equals(handleWay, FlowUserTaskExtData.TIMEOUT_AUTO_COMPLETE)) {
List<ExtensionAttribute> defaultAssigneeAttributes =
attributeMap.get(FlowConstant.TASK_TIMEOUT_DEFAULT_ASSIGNEE);
if (CollUtil.isEmpty(defaultAssigneeAttributes)) {
throw new MyRuntimeException("没有设置超时任务处理人!");
}
extraDataJson.put(FlowConstant.TASK_TIMEOUT_DEFAULT_ASSIGNEE, defaultAssigneeAttributes.get(0).getValue());
}
}
}
private void buildFlowTaskEmptyUserExtensionData(
Map<String, List<ExtensionAttribute>> attributeMap, JSONObject extraDataJson) {
List<ExtensionAttribute> emptyUserHandleWayAttributes = attributeMap.get(FlowConstant.EMPTY_USER_HANDLE_WAY);
if (CollUtil.isNotEmpty(emptyUserHandleWayAttributes)) {
String handleWay = emptyUserHandleWayAttributes.get(0).getValue();
extraDataJson.put(FlowConstant.EMPTY_USER_HANDLE_WAY, handleWay);
if (StrUtil.equals(handleWay, FlowUserTaskExtData.EMPTY_USER_TO_ASSIGNEE)) {
List<ExtensionAttribute> emptyUserToAssigneeAttributes = attributeMap.get(FlowConstant.EMPTY_USER_TO_ASSIGNEE);
if (CollUtil.isEmpty(emptyUserToAssigneeAttributes)) {
throw new MyRuntimeException("没有设置空审批人的指定处理人!");
}
extraDataJson.put(FlowConstant.EMPTY_USER_TO_ASSIGNEE, emptyUserToAssigneeAttributes.get(0).getValue());
}
}
}
private JSONObject buildFlowTaskExtensionData(UserTask userTask) {
JSONObject extraDataJson = this.buildFlowElementExtToJson(userTask);
Map<String, List<ExtensionAttribute>> attributeMap = userTask.getAttributes();
if (MapUtil.isEmpty(attributeMap)) {
return extraDataJson;
}
if (extraDataJson == null) {
extraDataJson = new JSONObject();
}
this.buildFlowTaskTimeoutExtensionData(attributeMap, extraDataJson);
this.buildFlowTaskEmptyUserExtensionData(attributeMap, extraDataJson);
List<ExtensionAttribute> rejectTypeAttributes = attributeMap.get(FlowConstant.USER_TASK_REJECT_TYPE_KEY);
if (CollUtil.isNotEmpty(rejectTypeAttributes)) {
extraDataJson.put(FlowConstant.USER_TASK_REJECT_TYPE_KEY, rejectTypeAttributes.get(0).getValue());
}
List<ExtensionAttribute> sendMsgTypeAttributes = attributeMap.get("sendMessageType");
if (CollUtil.isNotEmpty(sendMsgTypeAttributes)) {
ExtensionAttribute attribute = sendMsgTypeAttributes.get(0);
extraDataJson.put(FlowConstant.USER_TASK_NOTIFY_TYPES_KEY, StrUtil.split(attribute.getValue(), ","));
}
return extraDataJson;
}
private JSONObject buildUserCandidateGroupsExtensionElement(Map<String, List<ExtensionElement>> extensionMap) {
JSONObject jsonData = null;
List<ExtensionElement> elementCandidateGroupsList = extensionMap.get("userCandidateGroups");
if (CollUtil.isEmpty(elementCandidateGroupsList)) {
return jsonData;
}
jsonData = new JSONObject();
ExtensionElement ee = elementCandidateGroupsList.get(0);
jsonData.put(TYPE, ee.getAttributeValue(null, TYPE));
jsonData.put(VALUE, ee.getAttributeValue(null, VALUE));
return jsonData;
}
private JSONObject buildAssigneeListExtensionElement(Map<String, List<ExtensionElement>> extensionMap) {
JSONObject jsonData = null;
List<ExtensionElement> elementAssigneeList = extensionMap.get("assigneeList");
if (CollUtil.isEmpty(elementAssigneeList)) {
return jsonData;
}
ExtensionElement ee = elementAssigneeList.get(0);
Map<String, List<ExtensionElement>> childExtensionMap = ee.getChildElements();
if (MapUtil.isEmpty(childExtensionMap)) {
return jsonData;
}
List<ExtensionElement> assigneeElements = childExtensionMap.get("assignee");
if (CollUtil.isEmpty(assigneeElements)) {
return jsonData;
}
JSONArray assigneeIdArray = new JSONArray();
for (ExtensionElement e : assigneeElements) {
assigneeIdArray.add(e.getAttributeValue(null, ID));
}
jsonData = new JSONObject();
String assigneeType = ee.getAttributeValue(null, TYPE);
jsonData.put("assigneeType", assigneeType);
jsonData.put("assigneeList", assigneeIdArray);
return jsonData;
}
private List<JSONObject> buildDeptPostListExtensionElement(Map<String, List<ExtensionElement>> extensionMap) {
List<ExtensionElement> deptPostElements =
this.getMyExtensionElementList(extensionMap, "deptPostList", "deptPost");
if (CollUtil.isEmpty(deptPostElements)) {
return Collections.emptyList();
}
List<JSONObject> resultList = new LinkedList<>();
for (ExtensionElement e : deptPostElements) {
JSONObject deptPostJsonData = new JSONObject();
deptPostJsonData.put(ID, e.getAttributeValue(null, ID));
deptPostJsonData.put(TYPE, e.getAttributeValue(null, TYPE));
String postId = e.getAttributeValue(null, "postId");
if (postId != null) {
deptPostJsonData.put("postId", postId);
}
String deptPostId = e.getAttributeValue(null, "deptPostId");
if (deptPostId != null) {
deptPostJsonData.put("deptPostId", deptPostId);
}
resultList.add(deptPostJsonData);
}
return resultList;
}
private List<JSONObject> buildCopyListExtensionElement(Map<String, List<ExtensionElement>> extensionMap) {
List<ExtensionElement> copyElements =
this.getMyExtensionElementList(extensionMap, "copyItemList", "copyItem");
if (CollUtil.isEmpty(copyElements)) {
return Collections.emptyList();
}
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_SIBLING_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) {
if (extensionMap == null) {
return Collections.emptyList();
}
List<ExtensionElement> elementList = extensionMap.get(rootName);
if (CollUtil.isEmpty(elementList)) {
return Collections.emptyList();
}
if (StrUtil.isBlank(childName)) {
return elementList;
}
ExtensionElement ee = elementList.get(0);
Map<String, List<ExtensionElement>> childExtensionMap = ee.getChildElements();
if (MapUtil.isEmpty(childExtensionMap)) {
return Collections.emptyList();
}
List<ExtensionElement> childrenElements = childExtensionMap.get(childName);
if (CollUtil.isEmpty(childrenElements)) {
return Collections.emptyList();
}
return childrenElements;
}
private List<String> getAssigneeList(String executionId, String taskId) {
FlowMultiInstanceTrans flowMultiInstanceTrans =
flowMultiInstanceTransService.getByExecutionId(executionId, taskId);
String multiInstanceExecId;
if (flowMultiInstanceTrans == null) {
multiInstanceExecId = flowApiService.getTaskVariableStringWithSafe(
taskId, FlowConstant.MULTI_SIGN_TASK_EXECUTION_ID_VAR);
} else {
multiInstanceExecId = flowMultiInstanceTrans.getMultiInstanceExecId();
}
flowMultiInstanceTrans =
flowMultiInstanceTransService.getWithAssigneeListByMultiInstanceExecId(multiInstanceExecId);
return flowMultiInstanceTrans == null ? null
: StrUtil.split(flowMultiInstanceTrans.getAssigneeList(), ",");
}
private boolean buildTransferUserList(TaskInfo taskInfo, List<FlowUserInfoVo> resultUserMapList) {
BaseFlowIdentityExtHelper flowIdentityExtHelper = flowCustomExtFactory.getFlowIdentityExtHelper();
List<FlowTaskComment> taskCommentList = flowTaskCommentService.getFlowTaskCommentListByExecutionId(
taskInfo.getProcessInstanceId(), taskInfo.getId(), taskInfo.getExecutionId());
if (CollUtil.isEmpty(taskCommentList)) {
return false;
}
FlowTaskComment transferComment = null;
for (int i = taskCommentList.size() - 1; i >= 0; i--) {
FlowTaskComment comment = taskCommentList.get(i);
if (StrUtil.equalsAny(comment.getApprovalType(),
FlowApprovalType.TRANSFER, FlowApprovalType.INTERVENE)) {
transferComment = comment;
break;
}
}
if (transferComment == null || StrUtil.isBlank(transferComment.getDelegateAssignee())) {
return false;
}
Set<String> loginNameSet = new HashSet<>(StrUtil.split(transferComment.getDelegateAssignee(), ","));
resultUserMapList.addAll(flowIdentityExtHelper.getUserInfoListByUsernameSet(loginNameSet));
return true;
}
}

View File

@@ -0,0 +1,356 @@
package com.orangeforms.common.flow.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.page.PageMethod;
import com.orangeforms.common.core.annotation.DisableDataFilter;
import com.orangeforms.common.core.annotation.MyDataSourceResolver;
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
import com.orangeforms.common.core.base.service.BaseService;
import com.orangeforms.common.core.constant.ApplicationConstant;
import com.orangeforms.common.core.constant.GlobalDeletedFlag;
import com.orangeforms.common.core.object.*;
import com.orangeforms.common.core.util.DefaultDataSourceResolver;
import com.orangeforms.common.core.util.MyPageUtil;
import com.orangeforms.common.flow.constant.FlowTaskStatus;
import com.orangeforms.common.flow.constant.FlowConstant;
import com.orangeforms.common.flow.dao.FlowWorkOrderExtMapper;
import com.orangeforms.common.flow.dao.FlowWorkOrderMapper;
import com.orangeforms.common.flow.dto.FlowWorkOrderDto;
import com.orangeforms.common.flow.model.FlowEntry;
import com.orangeforms.common.flow.model.FlowWorkOrder;
import com.orangeforms.common.flow.model.FlowWorkOrderExt;
import com.orangeforms.common.flow.util.FlowOperationHelper;
import com.orangeforms.common.flow.vo.FlowWorkOrderVo;
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.util.BaseFlowIdentityExtHelper;
import com.orangeforms.common.flow.util.FlowCustomExtFactory;
import com.orangeforms.common.redis.util.CommonRedisUtil;
import com.orangeforms.common.sequence.wrapper.IdGeneratorWrapper;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@MyDataSourceResolver(
resolver = DefaultDataSourceResolver.class,
intArg = ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE)
@Service("flowWorkOrderService")
public class FlowWorkOrderServiceImpl extends BaseService<FlowWorkOrder, Long> implements FlowWorkOrderService {
@Autowired
private FlowWorkOrderMapper flowWorkOrderMapper;
@Autowired
private FlowWorkOrderExtMapper flowWorkOrderExtMapper;
@Autowired
private IdGeneratorWrapper idGenerator;
@Autowired
private FlowCustomExtFactory flowCustomExtFactory;
@Autowired
private FlowApiService flowApiService;
@Autowired
private FlowEntryService flowEntryService;
@Autowired
private CommonRedisUtil commonRedisUtil;
@Autowired
private FlowOperationHelper flowOperationHelper;
/**
* 返回当前Service的主表Mapper对象。
*
* @return 主表Mapper对象。
*/
@Override
protected BaseDaoMapper<FlowWorkOrder> mapper() {
return flowWorkOrderMapper;
}
/**
* 保存新增对象。
*
* @param instance 流程实例对象。
* @param dataId 流程实例的BusinessKey。
* @param onlineTableId 在线数据表的主键Id。
* @param tableName 面向静态表单所使用的表名。
* @return 新增的工作流工单对象。
*/
@Transactional(rollbackFor = Exception.class)
@Override
public FlowWorkOrder saveNew(ProcessInstance instance, Object dataId, Long onlineTableId, String tableName) {
// 正常插入流程工单数据。
FlowWorkOrder flowWorkOrder = this.createWith(instance);
flowWorkOrder.setWorkOrderCode(this.generateWorkOrderCode(instance.getProcessDefinitionKey()));
flowWorkOrder.setBusinessKey(dataId.toString());
flowWorkOrder.setOnlineTableId(onlineTableId);
flowWorkOrder.setTableName(tableName);
flowWorkOrder.setFlowStatus(FlowTaskStatus.SUBMITTED);
flowWorkOrderMapper.insert(flowWorkOrder);
return flowWorkOrder;
}
@Transactional(rollbackFor = Exception.class)
@Override
public FlowWorkOrder saveNewWithDraft(
ProcessInstance instance, Long onlineTableId, String tableName, String masterData, String slaveData) {
FlowWorkOrder flowWorkOrder = this.createWith(instance);
flowWorkOrder.setWorkOrderCode(this.generateWorkOrderCode(instance.getProcessDefinitionKey()));
flowWorkOrder.setOnlineTableId(onlineTableId);
flowWorkOrder.setTableName(tableName);
flowWorkOrder.setFlowStatus(FlowTaskStatus.DRAFT);
JSONObject draftData = new JSONObject();
if (masterData != null) {
draftData.put(FlowConstant.MASTER_DATA_KEY, masterData);
}
if (slaveData != null) {
draftData.put(FlowConstant.SLAVE_DATA_KEY, slaveData);
}
FlowWorkOrderExt flowWorkOrderExt =
BeanUtil.copyProperties(flowWorkOrder, FlowWorkOrderExt.class);
flowWorkOrderExt.setId(idGenerator.nextLongId());
flowWorkOrderExt.setDraftData(JSON.toJSONString(draftData));
flowWorkOrderExtMapper.insert(flowWorkOrderExt);
flowWorkOrderMapper.insert(flowWorkOrder);
return flowWorkOrder;
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updateDraft(Long workOrderId, String masterData, String slaveData) {
JSONObject draftData = new JSONObject();
if (masterData != null) {
draftData.put(FlowConstant.MASTER_DATA_KEY, masterData);
}
if (slaveData != null) {
draftData.put(FlowConstant.SLAVE_DATA_KEY, slaveData);
}
FlowWorkOrderExt flowWorkOrderExt = new FlowWorkOrderExt();
flowWorkOrderExt.setDraftData(JSON.toJSONString(draftData));
flowWorkOrderExt.setUpdateTime(new Date());
flowWorkOrderExtMapper.update(flowWorkOrderExt,
new LambdaQueryWrapper<FlowWorkOrderExt>().eq(FlowWorkOrderExt::getWorkOrderId, workOrderId));
}
/**
* 删除指定数据。
*
* @param workOrderId 主键Id。
* @return 成功返回true否则false。
*/
@Transactional(rollbackFor = Exception.class)
@Override
public boolean remove(Long workOrderId) {
return flowWorkOrderMapper.deleteById(workOrderId) == 1;
}
@Transactional(rollbackFor = Exception.class)
@Override
public void removeByProcessInstanceId(String processInstanceId) {
FlowWorkOrder filter = new FlowWorkOrder();
filter.setProcessInstanceId(processInstanceId);
super.removeBy(filter);
}
@Override
public List<FlowWorkOrder> getFlowWorkOrderList(FlowWorkOrder filter, String orderBy) {
if (filter == null) {
filter = new FlowWorkOrder();
}
TokenData tokenData = TokenData.takeFromRequest();
filter.setTenantId(tokenData.getTenantId());
filter.setAppCode(tokenData.getAppCode());
return flowWorkOrderMapper.getFlowWorkOrderList(filter, orderBy);
}
@Override
public List<FlowWorkOrder> getFlowWorkOrderListWithRelation(FlowWorkOrder filter, String orderBy) {
List<FlowWorkOrder> resultList = this.getFlowWorkOrderList(filter, orderBy);
this.buildRelationForDataList(resultList, MyRelationParam.dictOnly());
return resultList;
}
@Override
public FlowWorkOrder getFlowWorkOrderByProcessInstanceId(String processInstanceId) {
FlowWorkOrder filter = new FlowWorkOrder();
filter.setProcessInstanceId(processInstanceId);
return flowWorkOrderMapper.selectOne(new QueryWrapper<>(filter));
}
@Override
public boolean existByBusinessKey(String tableName, Object businessKey, boolean unfinished) {
LambdaQueryWrapper<FlowWorkOrder> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(FlowWorkOrder::getBusinessKey, businessKey.toString());
queryWrapper.eq(FlowWorkOrder::getTableName, tableName);
if (unfinished) {
queryWrapper.notIn(FlowWorkOrder::getFlowStatus,
FlowTaskStatus.FINISHED, FlowTaskStatus.CANCELLED, FlowTaskStatus.STOPPED);
}
return flowWorkOrderMapper.selectCount(queryWrapper) > 0;
}
@Override
public boolean existUnfinished(String processDefinitionKey, Object businessKey) {
LambdaQueryWrapper<FlowWorkOrder> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(FlowWorkOrder::getBusinessKey, businessKey.toString());
queryWrapper.eq(FlowWorkOrder::getProcessDefinitionKey, processDefinitionKey);
queryWrapper.notIn(FlowWorkOrder::getFlowStatus,
FlowTaskStatus.FINISHED, FlowTaskStatus.CANCELLED, FlowTaskStatus.STOPPED);
return flowWorkOrderMapper.selectCount(queryWrapper) > 0;
}
@DisableDataFilter
@Transactional(rollbackFor = Exception.class)
@Override
public void updateFlowStatusByProcessInstanceId(String processInstanceId, Integer flowStatus) {
if (flowStatus == null) {
return;
}
FlowWorkOrder flowWorkOrder = new FlowWorkOrder();
flowWorkOrder.setFlowStatus(flowStatus);
if (FlowTaskStatus.FINISHED != flowStatus) {
flowWorkOrder.setUpdateTime(new Date());
flowWorkOrder.setUpdateUserId(TokenData.takeFromRequest().getUserId());
}
LambdaQueryWrapper<FlowWorkOrder> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(FlowWorkOrder::getProcessInstanceId, processInstanceId);
flowWorkOrderMapper.update(flowWorkOrder, queryWrapper);
}
@DisableDataFilter
@Transactional(rollbackFor = Exception.class)
@Override
public void updateLatestApprovalStatusByProcessInstanceId(String processInstanceId, Integer approvalStatus) {
if (approvalStatus == null) {
return;
}
FlowWorkOrder flowWorkOrder = this.getFlowWorkOrderByProcessInstanceId(processInstanceId);
flowWorkOrder.setLatestApprovalStatus(approvalStatus);
flowWorkOrder.setUpdateTime(new Date());
flowWorkOrder.setUpdateUserId(TokenData.takeFromRequest().getUserId());
flowWorkOrderMapper.updateById(flowWorkOrder);
flowCustomExtFactory.getOnlineBusinessDataExtHelper().updateFlowStatus(flowWorkOrder);
// 处理在线表单工作流的自定义状态更新。
flowCustomExtFactory.getOnlineBusinessDataExtHelper().updateFlowStatus(flowWorkOrder);
}
@Override
public boolean hasDataPermOnFlowWorkOrder(String processInstanceId) {
// 开启数据权限,并进行验证。
boolean originalFlag = GlobalThreadLocal.setDataFilter(true);
long 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()));
}
});
}
@Override
public FlowWorkOrderExt getFlowWorkOrderExtByWorkOrderId(Long workOrderId) {
return flowWorkOrderExtMapper.selectOne(
new LambdaQueryWrapper<FlowWorkOrderExt>().eq(FlowWorkOrderExt::getWorkOrderId, workOrderId));
}
@Override
public List<FlowWorkOrderExt> getFlowWorkOrderExtByWorkOrderIds(Set<Long> workOrderIds) {
return flowWorkOrderExtMapper.selectList(
new LambdaQueryWrapper<FlowWorkOrderExt>().in(FlowWorkOrderExt::getWorkOrderId, workOrderIds));
}
@Transactional(rollbackFor = Exception.class)
@Override
public CallResult removeDraft(FlowWorkOrder flowWorkOrder) {
CallResult r = flowApiService.stopProcessInstance(flowWorkOrder.getProcessInstanceId(), "撤销草稿", true);
if (!r.isSuccess()) {
return r;
}
flowWorkOrderMapper.deleteById(flowWorkOrder.getWorkOrderId());
return CallResult.ok();
}
@Override
public MyPageData<FlowWorkOrderVo> getPagedWorkOrderListAndBuildData(
FlowWorkOrderDto flowWorkOrderDtoFilter, MyPageParam pageParam, MyOrderParam orderParam, String processDefinitionKey) {
PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize(), pageParam.getCount());
String orderBy = MyOrderParam.buildOrderBy(orderParam, FlowWorkOrder.class);
FlowWorkOrder filter = flowOperationHelper.makeWorkOrderFilter(flowWorkOrderDtoFilter, processDefinitionKey);
List<FlowWorkOrder> flowWorkOrderList = this.getFlowWorkOrderList(filter, orderBy);
MyPageData<FlowWorkOrderVo> resultData =
MyPageUtil.makeResponseData(flowWorkOrderList, FlowWorkOrderVo.class);
if (CollUtil.isEmpty(resultData.getDataList())) {
return resultData;
}
flowOperationHelper.buildWorkOrderApprovalStatus(processDefinitionKey, resultData.getDataList());
// 根据工单的提交用户名获取用户的显示名称,便于前端显示。
// 同时这也是一个如何通过插件方法将loginName映射到showName的示例
this.fillUserShowNameByLoginName(resultData.getDataList());
// 组装工单中需要返回给前端的流程任务数据。
flowOperationHelper.buildWorkOrderTaskInfo(resultData.getDataList());
return resultData;
}
private FlowWorkOrder createWith(ProcessInstance instance) {
TokenData tokenData = TokenData.takeFromRequest();
Date now = new Date();
FlowWorkOrder flowWorkOrder = new FlowWorkOrder();
flowWorkOrder.setWorkOrderId(idGenerator.nextLongId());
flowWorkOrder.setProcessDefinitionKey(instance.getProcessDefinitionKey());
flowWorkOrder.setProcessDefinitionName(instance.getProcessDefinitionName());
flowWorkOrder.setProcessDefinitionId(instance.getProcessDefinitionId());
flowWorkOrder.setProcessInstanceId(instance.getId());
flowWorkOrder.setSubmitUsername(tokenData.getLoginName());
flowWorkOrder.setDeptId(tokenData.getDeptId());
flowWorkOrder.setAppCode(tokenData.getAppCode());
flowWorkOrder.setTenantId(tokenData.getTenantId());
flowWorkOrder.setCreateUserId(tokenData.getUserId());
flowWorkOrder.setUpdateUserId(tokenData.getUserId());
flowWorkOrder.setCreateTime(now);
flowWorkOrder.setUpdateTime(now);
flowWorkOrder.setDeletedFlag(GlobalDeletedFlag.NORMAL);
return flowWorkOrder;
}
private String generateWorkOrderCode(String processDefinitionKey) {
FlowEntry flowEntry = flowEntryService.getFlowEntryFromCache(processDefinitionKey);
if (StrUtil.isBlank(flowEntry.getEncodedRule())) {
return null;
}
ColumnEncodedRule rule = JSON.parseObject(flowEntry.getEncodedRule(), ColumnEncodedRule.class);
if (rule.getIdWidth() == null) {
rule.setIdWidth(10);
}
return commonRedisUtil.generateTransId(
rule.getPrefix(), rule.getPrecisionTo(), rule.getMiddle(), rule.getIdWidth());
}
}

Some files were not shown because too many files have changed in this diff Show More