Parcourir la source

update warm-flow1.7.3 => 1.7.4 支持流程图悬浮窗

疯狂的狮子Li il y a 1 an
Parent
commit
679c83e837

+ 1 - 1
pom.xml

@@ -55,7 +55,7 @@
         <!-- 面向运行时的D-ORM依赖 -->
         <!-- 面向运行时的D-ORM依赖 -->
         <anyline.version>8.7.2-20250603</anyline.version>
         <anyline.version>8.7.2-20250603</anyline.version>
         <!-- 工作流配置 -->
         <!-- 工作流配置 -->
-        <warm-flow.version>1.7.3</warm-flow.version>
+        <warm-flow.version>1.7.4</warm-flow.version>
         <!-- mq配置 -->
         <!-- mq配置 -->
         <rocketmq.version>2.3.0</rocketmq.version>
         <rocketmq.version>2.3.0</rocketmq.version>
 
 

+ 4 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteUserServiceImpl.java

@@ -312,7 +312,10 @@ public class RemoteUserServiceImpl implements RemoteUserService {
             return new ArrayList<>();
             return new ArrayList<>();
         }
         }
         List<SysUserVo> list = userMapper.selectVoList(new LambdaQueryWrapper<SysUser>()
         List<SysUserVo> list = userMapper.selectVoList(new LambdaQueryWrapper<SysUser>()
-            .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName, SysUser::getEmail, SysUser::getPhonenumber)
+            .select(SysUser::getUserId, SysUser::getDeptId, SysUser::getUserName,
+                SysUser::getNickName, SysUser::getUserType, SysUser::getEmail,
+                SysUser::getPhonenumber, SysUser::getSex, SysUser::getStatus,
+                SysUser::getCreateTime)
             .eq(SysUser::getStatus, SystemConstants.NORMAL)
             .eq(SysUser::getStatus, SystemConstants.NORMAL)
             .in(SysUser::getUserId, userIds));
             .in(SysUser::getUserId, userIds));
         return MapstructUtils.convert(list, RemoteUserVo.class);
         return MapstructUtils.convert(list, RemoteUserVo.class);

+ 5 - 0
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java

@@ -73,4 +73,9 @@ public interface FlowConstant {
      */
      */
     String MESSAGE_NOTICE = "messageNotice";
     String MESSAGE_NOTICE = "messageNotice";
 
 
+    /**
+     * 任务状态
+     */
+    String WF_TASK_STATUS = "wf_task_status";
+
 }
 }

+ 1 - 7
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/CategoryNameTranslationImpl.java

@@ -26,12 +26,6 @@ public class CategoryNameTranslationImpl implements TranslationInterface<String>
 
 
     @Override
     @Override
     public String translation(Object key, String other) {
     public String translation(Object key, String other) {
-        Long id = null;
-        if (key instanceof String categoryId) {
-            id = Convert.toLong(categoryId);
-        } else if (key instanceof Long categoryId) {
-            id = categoryId;
-        }
-        return flwCategoryService.selectCategoryNameById(id);
+        return flwCategoryService.selectCategoryNameById(Convert.toLong(key));
     }
     }
 }
 }

+ 251 - 0
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwChartExtServiceImpl.java

@@ -0,0 +1,251 @@
+package org.dromara.workflow.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.common.core.service.DictService;
+import org.dromara.common.core.utils.DateUtils;
+import org.dromara.common.core.utils.ServletUtils;
+import org.dromara.common.core.utils.StreamUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.system.api.RemoteDeptService;
+import org.dromara.system.api.RemoteUserService;
+import org.dromara.system.api.domain.vo.RemoteUserVo;
+import org.dromara.warm.flow.core.dto.DefJson;
+import org.dromara.warm.flow.core.dto.NodeJson;
+import org.dromara.warm.flow.core.dto.PromptContent;
+import org.dromara.warm.flow.core.enums.NodeType;
+import org.dromara.warm.flow.core.utils.MapUtil;
+import org.dromara.warm.flow.orm.entity.FlowHisTask;
+import org.dromara.warm.flow.orm.mapper.FlowHisTaskMapper;
+import org.dromara.warm.flow.ui.service.ChartExtService;
+import org.dromara.workflow.common.ConditionalOnEnable;
+import org.dromara.workflow.common.constant.FlowConstant;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 流程图提示信息
+ *
+ * @author AprilWind
+ */
+@ConditionalOnEnable
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class FlwChartExtServiceImpl implements ChartExtService {
+
+    private final FlowHisTaskMapper flowHisTaskMapper;
+    private final DictService dictService;
+
+    @DubboReference
+    private RemoteUserService remoteUserService;
+    @DubboReference
+    private RemoteDeptService remoteDeptService;
+
+    /**
+     * 设置流程图提示信息
+     *
+     * @param defJson 流程定义json对象
+     */
+    @Override
+    public void execute(DefJson defJson) {
+        // 临时修复 后续版本将通过defjson获取流程实例ID
+        String[] parts = ServletUtils.getRequest().getRequestURI().split("/");
+        Long instanceId = Convert.toLong(parts[parts.length - 1]);
+
+        // 根据流程实例ID查询所有相关的历史任务列表
+        List<FlowHisTask> flowHisTasks = this.getHisTaskGroupedByNode(instanceId);
+        if (CollUtil.isEmpty(flowHisTasks)) {
+            return;
+        }
+
+        // 按节点编号(nodeCode)对历史任务进行分组
+        Map<String, List<FlowHisTask>> groupedByNode = StreamUtils.groupByKey(flowHisTasks, FlowHisTask::getNodeCode);
+
+        // 批量查询所有审批人的用户信息
+        List<RemoteUserVo> userDTOList = remoteUserService.selectListByIds(StreamUtils.toList(flowHisTasks, e -> Convert.toLong(e.getApprover())));
+
+        // 将查询到的用户列表转换为以用户ID为key的映射
+        Map<Long, RemoteUserVo> userMap = StreamUtils.toIdentityMap(userDTOList, RemoteUserVo::getUserId);
+
+        Map<String, String> dictType = dictService.getAllDictByDictType(FlowConstant.WF_TASK_STATUS);
+
+        // 遍历流程定义中的每个节点,调用处理方法,将对应节点的任务列表及用户信息传入,生成扩展提示内容
+        for (NodeJson nodeJson : defJson.getNodeList()) {
+            // 获取当前节点对应的历史任务列表,如果没有则返回空列表避免空指针
+            List<FlowHisTask> taskList = groupedByNode.get(nodeJson.getNodeCode());
+            if (CollUtil.isEmpty(taskList)) {
+                continue;
+            }
+            // 处理当前节点的扩展信息,包括构建审批人提示内容等
+            this.processNodeExtInfo(nodeJson, taskList, userMap, dictType);
+        }
+    }
+
+    /**
+     * 初始化流程图提示信息
+     *
+     * @param defJson 流程定义json对象
+     */
+    @Override
+    public void initPromptContent(DefJson defJson) {
+        defJson.setTopText("流程名称: " + defJson.getFlowName());
+        defJson.getNodeList().forEach(nodeJson -> {
+            nodeJson.setPromptContent(
+                new PromptContent()
+                    // 提示信息
+                    .setInfo(
+                        CollUtil.newArrayList(
+                            new PromptContent.InfoItem()
+                                .setPrefix("任务名称: ")
+                                .setContent(nodeJson.getNodeName())
+                                .setContentStyle(Map.of(
+                                    "border", "1px solid #d1e9ff",
+                                    "backgroundColor", "#e8f4ff",
+                                    "padding", "4px 8px",
+                                    "borderRadius", "4px"
+                                ))
+                                .setRowStyle(Map.of(
+                                    "fontWeight", "bold",
+                                    "margin", "0 0 6px 0",
+                                    "padding", "0 0 8px 0",
+                                    "borderBottom", "1px solid #ccc"
+                                ))
+                        )
+                    )
+                    // 弹窗样式
+                    .setDialogStyle(MapUtil.mergeAll(
+                        "position", "absolute",
+                        "backgroundColor", "#fff",
+                        "border", "1px solid #ccc",
+                        "borderRadius", "4px",
+                        "boxShadow", "0 2px 8px rgba(0, 0, 0, 0.15)",
+                        "padding", "8px 12px",
+                        "fontSize", "14px",
+                        "zIndex", "1000",
+                        "maxWidth", "500px",
+                        "overflowY", "visible",
+                        "overflowX", "hidden",
+                        "color", "#333",
+                        "pointerEvents", "auto",
+                        "scrollbarWidth", "thin"
+                    ))
+            );
+        });
+    }
+
+    /**
+     * 处理节点的扩展信息,构建用于流程图悬浮提示的内容
+     *
+     * @param nodeJson 当前节点对象
+     * @param taskList 当前节点对应的历史审批任务列表
+     */
+    private void processNodeExtInfo(NodeJson nodeJson, List<FlowHisTask> taskList, Map<Long, RemoteUserVo> userMap, Map<String, String> dictType) {
+
+        // 获取节点提示内容对象中的 info 列表,用于追加提示项
+        List<PromptContent.InfoItem> info = nodeJson.getPromptContent().getInfo();
+
+        // 遍历所有任务记录,构建提示内容
+        for (FlowHisTask task : taskList) {
+            RemoteUserVo userDTO = userMap.get(Convert.toLong(task.getApprover()));
+            if (ObjectUtil.isEmpty(userDTO)) {
+                continue;
+            }
+
+            // 查询用户所属部门名称
+            String deptName = remoteDeptService.selectDeptNameByIds(Convert.toStr(userDTO.getDeptId()));
+
+            // 添加标题项,如:👤 张三(市场部)
+            info.add(new PromptContent.InfoItem()
+                .setPrefix(StringUtils.format("👥 {}({})", userDTO.getNickName(), deptName))
+                .setPrefixStyle(Map.of(
+                    "fontWeight", "bold",
+                    "fontSize", "15px",
+                    "color", "#333"
+                ))
+                .setRowStyle(Map.of(
+                    "margin", "8px 0",
+                    "borderBottom", "1px dashed #ccc"
+                ))
+            );
+
+            // 添加具体信息项:账号、耗时、时间
+            info.add(buildInfoItem("用户账号", userDTO.getUserName()));
+            info.add(buildInfoItem("审批状态", dictType.get(task.getFlowStatus())));
+            info.add(buildInfoItem("审批耗时", DateUtils.getTimeDifference(task.getUpdateTime(), task.getCreateTime())));
+            info.add(buildInfoItem("办理时间", DateUtils.formatDateTime(task.getUpdateTime())));
+        }
+    }
+
+    /**
+     * 构建单条提示内容对象 InfoItem,用于悬浮窗显示(key: value)
+     *
+     * @param key   字段名(作为前缀)
+     * @param value 字段值
+     * @return 提示项对象
+     */
+    private PromptContent.InfoItem buildInfoItem(String key, String value) {
+        return new PromptContent.InfoItem()
+            // 前缀
+            .setPrefix(key + ": ")
+            // 前缀样式
+            .setPrefixStyle(Map.of(
+                "textAlign", "right",
+                "color", "#444",
+                "userSelect", "none",
+                "display", "inline-block",
+                "width", "100px",
+                "paddingRight", "8px",
+                "fontWeight", "500",
+                "fontSize", "14px",
+                "lineHeight", "24px",
+                "verticalAlign", "middle"
+            ))
+            // 内容
+            .setContent(value)
+            // 内容样式
+            .setContentStyle(Map.of(
+                "backgroundColor", "#f7faff",
+                "color", "#005cbf",
+                "padding", "4px 8px",
+                "fontSize", "14px",
+                "borderRadius", "4px",
+                "whiteSpace", "normal",
+                "border", "1px solid #d0e5ff",
+                "userSelect", "text",
+                "lineHeight", "20px"
+            ))
+            // 行样式
+            .setRowStyle(Map.of(
+                "color", "#222",
+                "alignItems", "center",
+                "display", "flex",
+                "marginBottom", "6px",
+                "fontWeight", "400",
+                "fontSize", "14px"
+            ));
+    }
+
+    /**
+     * 根据流程实例ID获取历史任务列表
+     *
+     * @param instanceId 流程实例ID
+     * @return 历史任务列表
+     */
+    public List<FlowHisTask> getHisTaskGroupedByNode(Long instanceId) {
+        LambdaQueryWrapper<FlowHisTask> wrapper = Wrappers.lambdaQuery();
+        wrapper.eq(FlowHisTask::getInstanceId, instanceId)
+            .eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey())
+            .orderByDesc(FlowHisTask::getCreateTime, FlowHisTask::getUpdateTime);
+        return flowHisTaskMapper.selectList(wrapper);
+    }
+
+}

+ 3 - 3
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java

@@ -304,9 +304,9 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
         }
         }
         //历史任务
         //历史任务
         LambdaQueryWrapper<FlowHisTask> wrapper = Wrappers.lambdaQuery();
         LambdaQueryWrapper<FlowHisTask> wrapper = Wrappers.lambdaQuery();
-        wrapper.eq(FlowHisTask::getInstanceId, instanceId);
-        wrapper.eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey());
-        wrapper.orderByDesc(FlowHisTask::getCreateTime).orderByDesc(FlowHisTask::getUpdateTime);
+        wrapper.eq(FlowHisTask::getInstanceId, instanceId)
+            .eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey())
+            .orderByDesc(FlowHisTask::getCreateTime, FlowHisTask::getUpdateTime);
         List<FlowHisTask> flowHisTasks = flowHisTaskMapper.selectList(wrapper);
         List<FlowHisTask> flowHisTasks = flowHisTaskMapper.selectList(wrapper);
         if (CollUtil.isNotEmpty(flowHisTasks)) {
         if (CollUtil.isNotEmpty(flowHisTasks)) {
             list.addAll(BeanUtil.copyToList(flowHisTasks, FlowHisTaskVo.class));
             list.addAll(BeanUtil.copyToList(flowHisTasks, FlowHisTaskVo.class));

+ 3 - 2
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java

@@ -2,6 +2,7 @@ package org.dromara.workflow.service.impl;
 
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.convert.Convert;
 import cn.hutool.core.lang.Pair;
 import cn.hutool.core.lang.Pair;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
@@ -249,10 +250,10 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand
         try {
         try {
             String[] parts = storageId.split(StrUtil.COLON, 2);
             String[] parts = storageId.split(StrUtil.COLON, 2);
             if (parts.length < 2) {
             if (parts.length < 2) {
-                return Pair.of(TaskAssigneeEnum.USER, Long.valueOf(parts[0]));
+                return Pair.of(TaskAssigneeEnum.USER, Convert.toLong(parts[0]));
             } else {
             } else {
                 TaskAssigneeEnum type = TaskAssigneeEnum.fromCode(parts[0] + StrUtil.COLON);
                 TaskAssigneeEnum type = TaskAssigneeEnum.fromCode(parts[0] + StrUtil.COLON);
-                return Pair.of(type, Long.valueOf(parts[1]));
+                return Pair.of(type, Convert.toLong(parts[1]));
             }
             }
         } catch (Exception e) {
         } catch (Exception e) {
             log.warn("解析 storageId 失败,格式非法:{},错误信息:{}", storageId, e.getMessage());
             log.warn("解析 storageId 失败,格式非法:{},错误信息:{}", storageId, e.getMessage());

+ 6 - 3
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java

@@ -534,7 +534,10 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
             //构建以下节点数据
             //构建以下节点数据
             List<Task> buildNextTaskList = StreamUtils.toList(nextNodeList, node -> taskService.addTask(node, instance, definition, FlowParams.build()));
             List<Task> buildNextTaskList = StreamUtils.toList(nextNodeList, node -> taskService.addTask(node, instance, definition, FlowParams.build()));
             //办理人变量替换
             //办理人变量替换
-            ExpressionUtil.evalVariable(buildNextTaskList, mergeVariable);
+            ExpressionUtil.evalVariable(buildNextTaskList,
+                FlowParams.build()
+                    .variable(mergeVariable)
+            );
             for (FlowNode flowNode : nextFlowNodes) {
             for (FlowNode flowNode : nextFlowNodes) {
                 buildNextTaskList.stream().filter(t -> t.getNodeCode().equals(flowNode.getNodeCode())).findFirst().ifPresent(t -> {
                 buildNextTaskList.stream().filter(t -> t.getNodeCode().equals(flowNode.getNodeCode())).findFirst().ifPresent(t -> {
                     if (CollUtil.isNotEmpty(t.getPermissionList())) {
                     if (CollUtil.isNotEmpty(t.getPermissionList())) {
@@ -718,7 +721,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
         for (Map.Entry<Long, List<User>> entry : listMap.entrySet()) {
         for (Map.Entry<Long, List<User>> entry : listMap.entrySet()) {
             List<User> value = entry.getValue();
             List<User> value = entry.getValue();
             if (CollUtil.isNotEmpty(value)) {
             if (CollUtil.isNotEmpty(value)) {
-                List<RemoteUserVo> userDtoList = remoteUserService.selectListByIds(StreamUtils.toList(value, e -> Long.valueOf(e.getProcessedBy())));
+                List<RemoteUserVo> userDtoList = remoteUserService.selectListByIds(StreamUtils.toList(value, e -> Convert.toLong(e.getProcessedBy())));
                 map.put(entry.getKey(), userDtoList);
                 map.put(entry.getKey(), userDtoList);
             }
             }
         }
         }
@@ -737,7 +740,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
         if (CollUtil.isEmpty(userList)) {
         if (CollUtil.isEmpty(userList)) {
             return Collections.emptyList();
             return Collections.emptyList();
         }
         }
-        return remoteUserService.selectListByIds(StreamUtils.toList(userList, e -> Long.valueOf(e.getProcessedBy())));
+        return remoteUserService.selectListByIds(StreamUtils.toList(userList, e -> Convert.toLong(e.getProcessedBy())));
     }
     }
 
 
     /**
     /**

+ 1 - 1
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java

@@ -147,7 +147,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService {
     public void processHandler(ProcessEvent processEvent) {
     public void processHandler(ProcessEvent processEvent) {
         TenantHelper.dynamic(processEvent.getTenantId(), () -> {
         TenantHelper.dynamic(processEvent.getTenantId(), () -> {
             log.info("当前任务执行了{}", processEvent.toString());
             log.info("当前任务执行了{}", processEvent.toString());
-            TestLeave testLeave = baseMapper.selectById(Long.valueOf(processEvent.getBusinessId()));
+            TestLeave testLeave = baseMapper.selectById(Convert.toLong(processEvent.getBusinessId()));
             testLeave.setStatus(processEvent.getStatus());
             testLeave.setStatus(processEvent.getStatus());
             // 用于例如审批附件 审批意见等 存储到业务表内 自行根据业务实现存储流程
             // 用于例如审批附件 审批意见等 存储到业务表内 自行根据业务实现存储流程
             Map<String, Object> params = processEvent.getParams();
             Map<String, Object> params = processEvent.getParams();

+ 10 - 10
script/sql/ry-workflow.sql

@@ -29,7 +29,7 @@ CREATE TABLE `flow_node`
     `definition_id`   bigint          NOT NULL COMMENT '流程定义id',
     `definition_id`   bigint          NOT NULL COMMENT '流程定义id',
     `node_code`       varchar(100)    NOT NULL COMMENT '流程节点编码',
     `node_code`       varchar(100)    NOT NULL COMMENT '流程节点编码',
     `node_name`       varchar(100)  DEFAULT NULL COMMENT '流程节点名称',
     `node_name`       varchar(100)  DEFAULT NULL COMMENT '流程节点名称',
-    `permission_flag` varchar(200)  DEFAULT NULL COMMENT '权限标识(权限类型:权限标识,可以多个,用逗号隔开)',
+    `permission_flag` varchar(200)  DEFAULT NULL COMMENT '权限标识(权限类型:权限标识,可以多个,用@@隔开)',
     `node_ratio`      decimal(6, 3) DEFAULT NULL COMMENT '流程签署比例值',
     `node_ratio`      decimal(6, 3) DEFAULT NULL COMMENT '流程签署比例值',
     `coordinate`      varchar(100)  DEFAULT NULL COMMENT '坐标',
     `coordinate`      varchar(100)  DEFAULT NULL COMMENT '坐标',
     `any_node_skip`   varchar(100)  DEFAULT NULL COMMENT '任意结点跳转',
     `any_node_skip`   varchar(100)  DEFAULT NULL COMMENT '任意结点跳转',
@@ -42,7 +42,7 @@ CREATE TABLE `flow_node`
     `version`         varchar(20)     NOT NULL COMMENT '版本',
     `version`         varchar(20)     NOT NULL COMMENT '版本',
     `create_time`     datetime      DEFAULT NULL COMMENT '创建时间',
     `create_time`     datetime      DEFAULT NULL COMMENT '创建时间',
     `update_time`     datetime      DEFAULT NULL COMMENT '更新时间',
     `update_time`     datetime      DEFAULT NULL COMMENT '更新时间',
-    `ext`             text          COMMENT '扩展属性',
+    `ext`             text          COMMENT '节点扩展属性',
     `del_flag`        char(1)       DEFAULT '0' COMMENT '删除标志',
     `del_flag`        char(1)       DEFAULT '0' COMMENT '删除标志',
     `tenant_id`       varchar(40)   DEFAULT NULL COMMENT '租户id',
     `tenant_id`       varchar(40)   DEFAULT NULL COMMENT '租户id',
     PRIMARY KEY (`id`) USING BTREE
     PRIMARY KEY (`id`) USING BTREE
@@ -108,25 +108,25 @@ CREATE TABLE `flow_task`
 
 
 CREATE TABLE `flow_his_task`
 CREATE TABLE `flow_his_task`
 (
 (
-    `id`               bigint(20)          NOT NULL COMMENT '主键id',
-    `definition_id`    bigint(20)          NOT NULL COMMENT '对应flow_definition表的id',
-    `instance_id`      bigint(20)          NOT NULL COMMENT '对应flow_instance表的id',
-    `task_id`          bigint(20)          NOT NULL COMMENT '对应flow_task表的id',
+    `id`               bigint(20)                   NOT NULL COMMENT '主键id',
+    `definition_id`    bigint(20)                   NOT NULL COMMENT '对应flow_definition表的id',
+    `instance_id`      bigint(20)                   NOT NULL COMMENT '对应flow_instance表的id',
+    `task_id`          bigint(20)                   NOT NULL COMMENT '对应flow_task表的id',
     `node_code`        varchar(100)                 DEFAULT NULL COMMENT '开始节点编码',
     `node_code`        varchar(100)                 DEFAULT NULL COMMENT '开始节点编码',
     `node_name`        varchar(100)                 DEFAULT NULL COMMENT '开始节点名称',
     `node_name`        varchar(100)                 DEFAULT NULL COMMENT '开始节点名称',
     `node_type`        tinyint(1)                   DEFAULT NULL COMMENT '开始节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)',
     `node_type`        tinyint(1)                   DEFAULT NULL COMMENT '开始节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)',
     `target_node_code` varchar(200)                 DEFAULT NULL COMMENT '目标节点编码',
     `target_node_code` varchar(200)                 DEFAULT NULL COMMENT '目标节点编码',
     `target_node_name` varchar(200)                 DEFAULT NULL COMMENT '结束节点名称',
     `target_node_name` varchar(200)                 DEFAULT NULL COMMENT '结束节点名称',
     `approver`         varchar(40)                  DEFAULT NULL COMMENT '审批者',
     `approver`         varchar(40)                  DEFAULT NULL COMMENT '审批者',
-    `cooperate_type`   tinyint(1)          NOT NULL DEFAULT '0' COMMENT '协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)',
+    `cooperate_type`   tinyint(1)                   NOT NULL DEFAULT '0' COMMENT '协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)',
     `collaborator`     varchar(40)                  DEFAULT NULL COMMENT '协作人',
     `collaborator`     varchar(40)                  DEFAULT NULL COMMENT '协作人',
-    `skip_type`        varchar(10)         NOT NULL COMMENT '流转类型(PASS通过 REJECT退回 NONE无动作)',
-    `flow_status`      varchar(20)         NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)',
+    `skip_type`        varchar(10)                  NOT NULL COMMENT '流转类型(PASS通过 REJECT退回 NONE无动作)',
+    `flow_status`      varchar(20)                  NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)',
     `form_custom`      char(1)                      DEFAULT 'N' COMMENT '审批表单是否自定义(Y是 N否)',
     `form_custom`      char(1)                      DEFAULT 'N' COMMENT '审批表单是否自定义(Y是 N否)',
     `form_path`        varchar(100)                 DEFAULT NULL COMMENT '审批表单路径',
     `form_path`        varchar(100)                 DEFAULT NULL COMMENT '审批表单路径',
     `message`          varchar(500)                 DEFAULT NULL COMMENT '审批意见',
     `message`          varchar(500)                 DEFAULT NULL COMMENT '审批意见',
     `variable`         TEXT                         DEFAULT NULL COMMENT '任务变量',
     `variable`         TEXT                         DEFAULT NULL COMMENT '任务变量',
-    `ext`              varchar(500)                 DEFAULT NULL COMMENT '业务详情 存业务表对象json字符串',
+    `ext`              TEXT                         DEFAULT NULL COMMENT '业务详情 存业务表对象json字符串',
     `create_time`      datetime                     DEFAULT NULL COMMENT '任务开始时间',
     `create_time`      datetime                     DEFAULT NULL COMMENT '任务开始时间',
     `update_time`      datetime                     DEFAULT NULL COMMENT '审批完成时间',
     `update_time`      datetime                     DEFAULT NULL COMMENT '审批完成时间',
     `del_flag`         char(1)                      DEFAULT '0' COMMENT '删除标志',
     `del_flag`         char(1)                      DEFAULT '0' COMMENT '删除标志',