背景:小程序開發申請流程。使用flowable流程框架。用戶需要在后臺統攬用戶申請的匯總表。
設計思路:通過查詢流程實例分頁查詢獲取數據, 其中可以通過查詢條件進行查詢,查詢條件是流程申請時添加到流程變量當中的,方便進行查詢
具體內容。
- 涉及到前端頁面和后端代碼,還有導出部分實現。
- PC頁面
- 后端代碼:此部分為具體實現流程。
@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);
對入參進行處理
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")));}// 判斷項目idif (ObjectUtil.isNotEmpty(params.get("projectId"))) {query.variableValueEquals("projectId", params.get("projectId"));}// 判斷病種idif (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();}}
}
導出方案及實現
- 核心內容: 大模型給的方案中,我使用了分批導出。相比于延長超時時間、分頁導出等更合理。是使用前端進行導出的方式,只獲取后端接口數據。
/** 導出按鈕操作 */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('導出失敗,請重試');}}},