flowable汇总查询方式

背景:小程序开发申请流程。使用flowable流程框架。用户需要在后台统揽用户申请的汇总表。

设计思路:通过查询流程实例分页查询获取数据其中可以通过查询条件进行查询查询条件是流程申请时添加到流程变量当中的,方便进行查询

具体内容。

  • 涉及到前端页面和后端代码,还有导出部分实现。
  • PC页面
  • 后端代码:此部分为具体实现流程。
java 复制代码
@Override
public TableDataInfo<WfTaskVo> selectPageInitiatorProcessList(ProcessQuery processQuery, PageQuery pageQuery) {
    Page<WfTaskVo> page = new Page<>();
    HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery()
            .orderByProcessInstanceStartTime()
            .desc();
    // 使用buildProcessSearch拼接查询条件
    ProcessUtils.buildProcessSearch(historicProcessInstanceQuery, processQuery);
    int offset = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1);
    List<HistoricProcessInstance> historicProcessInstances = historicProcessInstanceQuery
            .includeProcessVariables()
            .listPage(offset, pageQuery.getPageSize());
    page.setTotal(historicProcessInstanceQuery.count());
    List<WfTaskVo> taskVoList = new ArrayList<>();
    for (HistoricProcessInstance hisIns : historicProcessInstances) {
        WfTaskVo taskVo = new WfTaskVo();
        taskVo.setProcInsId(hisIns.getId());
        taskVo.setProcDefId(hisIns.getProcessDefinitionId());
        taskVo.setProcDefName(hisIns.getProcessDefinitionName());
        taskVo.setCreateTime(hisIns.getStartTime());
        taskVo.setFinishTime(hisIns.getEndTime());
        // 计算耗时
        if (Objects.nonNull(hisIns.getEndTime())) {
            taskVo.setDuration(DateUtils.getDatePoor(hisIns.getEndTime(), hisIns.getStartTime()));
        } else {
            taskVo.setDuration(DateUtils.getDatePoor(DateUtils.getNowDate(), hisIns.getStartTime()));
        }
        // 获取发起人信息
        Long userId = Long.parseLong(hisIns.getStartUserId());
        String nickName = userService.selectNickNameById(userId);
        taskVo.setStartUserId(userId);
        taskVo.setStartUserName(nickName);
        Object name = hisIns.getProcessVariables().get("name");
        if (name != null) {
            taskVo.setName(name.toString());
        }
        Object projectId = hisIns.getProcessVariables().get(BusiConstants.PROJECT_ID);
        if (projectId != null) {
            taskVo.setProjectId(projectId.toString());
        }
        taskVoList.add(taskVo);
    }
    page.setRecords(taskVoList);
    return TableDataInfo.build(page);
}
  • 此部分为添加的查询条件处理。ProcessUtils.buildProcessSearch(historicProcessInstanceQuery, processQuery);对入参进行处理
java 复制代码
public static void buildProcessSearch(Query<?, ?> query, ProcessQuery process) {
     if (query instanceof ProcessDefinitionQuery) {
         buildProcessDefinitionSearch((ProcessDefinitionQuery) query, process);
     } else if (query instanceof TaskQuery) {
         buildTaskSearch((TaskQuery) query, process);
     } else if (query instanceof HistoricTaskInstanceQuery) {
         buildHistoricTaskInstanceSearch((HistoricTaskInstanceQuery) query, process);
     } else if (query instanceof HistoricProcessInstanceQuery) {
         buildHistoricProcessInstanceSearch((HistoricProcessInstanceQuery) query, process);
     }
}
...
public static void buildHistoricProcessInstanceSearch(HistoricProcessInstanceQuery query, ProcessQuery process) {
    Map<String, Object> params = process.getParams();
    // 流程标识
    if (StringUtils.isNotBlank(process.getProcessKey())) {
        query.processDefinitionKey(process.getProcessKey());
    }
    // 流程名称
    if (StringUtils.isNotBlank(process.getProcessName())) {
        query.processDefinitionName(process.getProcessName());
    }
    // 流程名称
    if (StringUtils.isNotBlank(process.getCategory())) {
        query.processDefinitionCategory(process.getCategory());
    }
    if (params.get("beginTime") != null && params.get("endTime") != null) {
        query.startedAfter(DateUtils.parseDate(params.get("beginTime")));
        query.startedBefore(DateUtils.parseDate(params.get("endTime")));
    }
    // 判断项目id
    if (ObjectUtil.isNotEmpty(params.get("projectId"))) {
        query.variableValueEquals("projectId", params.get("projectId"));
    }
    // 判断病种id
    if (ObjectUtil.isNotEmpty(params.get("diseaseInfoId"))) {
        query.variableValueEquals("diseaseInfoId", params.get("diseaseInfoId"));
    }
    // 模糊查询姓名
    if (ObjectUtil.isNotEmpty(params.get("name"))) {
        query.variableValueLike("name", "%" + params.get("name").toString() + "%");
    }
    // 查询省份
    if (ObjectUtil.isNotEmpty(params.get("provinceCode"))) {
        query.variableValueEquals("provinceCode", params.get("provinceCode"));
    }
    // 流程状态(已完成未完成)
    if (ObjectUtil.isNotEmpty(params.get("status"))) {
        if ("1".equals(params.get("status"))) {
            query.finished();
        }else {
            query.unfinished();
        }
    }
}

导出方案及实现

  • 核心内容: 大模型给的方案中,我使用了分批导出。相比于延长超时时间、分页导出等更合理。是使用前端进行导出的方式,只获取后端接口数据。
js 复制代码
 /** 导出按钮操作 */
    async handleExport() {
      try {
        // 显示进度对话框
        this.exportProgress.visible = true;
        this.exportProgress.percentage = 0;
        this.exportProgress.currentBatch = 0;
        this.exportProgress.totalBatches = 0;
        this.exportProgress.currentCount = 0;
        this.exportProgress.totalCount = 0;

        const batchSize = 1000; // 每批1000条数据
        let allData = [];
        let currentPage = 1;
        let hasMore = true;

        // 构建基础查询参数
        const baseParams = {
          name: this.queryParams.name,
          projectId: this.queryParams.projectId,
          startUserName: this.queryParams.startUserName,
          status: this.queryParams.status
        };

        // 添加日期范围参数
        if (this.dateRange && this.dateRange.length === 2) {
          baseParams.beginTime = this.dateRange[0];
          baseParams.endTime = this.dateRange[1];
        } else {
          // 如果没有选择时间范围,设置默认值(最近30天)
          const endDate = new Date();
          const startDate = new Date();
          startDate.setDate(startDate.getDate() - 30);
          baseParams.beginTime = startDate.toISOString().slice(0, 19).replace('T', ' ');
          baseParams.endTime = endDate.toISOString().slice(0, 19).replace('T', ' ');
        }

        // 先获取总数,用于计算进度
        const countResponse = await fetchInitiatorList({
          pageNum: 1,
          pageSize: 1,
          params: baseParams
        });

        if (countResponse.code === 200) {
          this.exportProgress.totalCount = countResponse.total || 0;
          this.exportProgress.totalBatches = Math.ceil(this.exportProgress.totalCount / batchSize);
        }

        // 分批获取数据
        while (hasMore) {
          const batchParams = {
            pageNum: currentPage,
            pageSize: batchSize,
            params: baseParams
          };

          // 更新进度信息
          this.exportProgress.currentBatch = currentPage;
          this.exportProgress.currentCount = allData.length;
          this.exportProgress.percentage = Math.round((allData.length / this.exportProgress.totalCount) * 100);

          const response = await fetchInitiatorList(batchParams);

          if (response.code === 200 && response.rows && response.rows.length > 0) {
            allData = allData.concat(response.rows);
            currentPage++;

            // 检查是否还有更多数据
            hasMore = response.rows.length === batchSize;

            // 添加小延迟,避免请求过于频繁
            await new Promise((resolve) => setTimeout(resolve, 100));
          } else {
            hasMore = false;
          }
        }

        if (allData.length > 0) {
          // 更新最终进度
          this.exportProgress.currentCount = allData.length;
          this.exportProgress.percentage = 100;

          // 显示最终进度
          this.$message.info(`数据获取完成,共 ${allData.length} 条,正在生成Excel文件...`);

          // 格式化数据为Excel格式
          const excelData = this.formatDataForExcel(allData);

          // 生成Excel文件
          this.generateAndDownloadExcel(excelData);

          this.$message.success(`导出成功,共导出 ${allData.length} 条数据`);
        } else {
          this.$message.warning('没有数据可导出');
        }

        // 关闭进度对话框
        setTimeout(() => {
          this.exportProgress.visible = false;
        }, 2000);
      } catch (error) {
        console.error('导出失败:', error);

        // 关闭进度对话框
        this.exportProgress.visible = false;

        if (error.message && error.message.includes('timeout')) {
          this.$message.error('导出超时,请尝试缩小查询范围或联系管理员');
        } else {
          this.$message.error('导出失败,请重试');
        }
      }
    },