如何通过进度条来展示异步线程的处理进度原创
金蝶云社区-范老师加油
范老师加油
7人赞赏了该文章 1,837次浏览 未经作者许可,禁止转载编辑于2022年12月25日 22:03:49
summary-icon摘要由AI智能服务提供

本文介绍了在苍穹开发平台中,利用线程池处理后台任务时,通过前端进度条展示任务进度的实现方法。首先,明确进度条仅作为前端展示工具,不直接处理任务。其次,通过构造测试数据和弹窗进度条,以及在后端使用线程池异步处理任务,并实时更新进度条状态,展示了任务进度从开始到完成的整个过程。最后,展示了实现后的效果图,并提供了开发环境版本和参考资料。

关键词:进度条,线程池,任务进度

一、需求

   在苍穹开发中,我们有时会用到线程池来处理一些后台任务,而这些任务往往我们不清楚已经处理到哪步了,这个时候想通过进度条来展示。

二、思路与方案

  进度条是苍穹的一个前端组件,之前开发中,有人以为加了这个进度条就能自动转后台任务,这是不正确的。进度条就像电脑显示器,它并不会自动处理任务,它只是前台展示的作用,我们可以利用它来展示我们后台任务处理到多少百分比。它可以让我们的交互变的更加友好。

  

  而实际的处理逻辑,我们可以通过线程池或者调度任务来处理,这里介绍下进度条如何来展示异步线程的处理。

为了方便演示,我们这次通过构造100条数据,然后异步线程来处理这100条数据从暂存状态变成审核状态,通过这种方式来展示进度条处理情况。

三、实现过程


1  构造测试单据及单据数据

我们在开发平台新建一个单据,为了方便后续对每批次的处理的数据不一样,我在上面加了一个批次号字段。


image.png

下一步就是构造简单的数据,通过下面这段代码,我就往单据里面灌了100条测试数据

 public void itemClick(ItemClickEvent evt) {
		 String key =evt.getItemKey();
		 if("bidt_baritemap".equals(key)) {
			 DynamicObject[] dynList=new DynamicObject[100];
			 for(int i=0;i<100;i++) {
				 DynamicObject newObject =BusinessDataServiceHelper.newDynamicObject("bidt_process_bar_bill");
			     newObject.set("billno", "test"+i);
			     newObject.set("billstatus", "A");
			     dynList[i]=newObject;
			 }
		     SaveServiceHelper.saveOperate("bidt_process_bar_bill",dynList);
		 }
	 }


  2   构造弹窗进度条

 我又建立了一个动态表单,通过动态表单里的进度条来显示后台任务的处理进度了,这里的进度条,我设置成了每1秒刷新一次。

image.png


3 在单据列表上加上弹窗按钮

 回到刚才我们创建单据,我在该单据的页面添加一个按钮,可以通过这个按钮弹窗显示有进度条的动态表单。

image.png

我在列表上加上插件,这个插件里面主要是两段逻辑,

     在弹窗前把所有待处理的数据,记录批次号

     弹窗-弹出进度条的动态表单

public class LinkProcessForm extends AbstractListPlugin{
	 public void itemClick(ItemClickEvent evt) {
	        super.itemClick(evt);
	        String itemKey = evt.getItemKey();
	        if("bidt_processform".equals(itemKey)){
	        	long traceId=TraceIdUtil.getCurrentTraceId();//获取当前的traceId,并把traceId当作批次号
	        	ListSelectedRowCollection selectList=((IListView) this.getView()).getSelectedRows();
	        	DynamicObject[] dynList=new DynamicObject[selectList.size()];//获取列表上所有点选的数据
	        	for(int i=0;i<selectList.size();i++) {
	        		long id=(long) selectList.get(i).getPrimaryKeyValue();
	        		DynamicObject dynObject=BusinessDataServiceHelper.loadSingle(id, "bidt_process_bar_bill");
	        		dynObject.set("bidt_batch_no", traceId);
	        		dynList[i]=dynObject;
	        	}
	        	SaveServiceHelper.save(dynList);//将所有点选的数据改变它们的批次号,方便异步线程来处理
	        	
	        	//打开进度条弹窗
	            FormShowParameter formShowParameter = new FormShowParameter();
	            formShowParameter.setFormId("bidt_progressform");
	            formShowParameter.setCustomParam("batchNo", traceId);//把当前的批次号传给子页面,
	            formShowParameter.setCustomParam("totalCount", selectList.size());//把当前的总条数传给子页面
	            formShowParameter.getOpenStyle().setShowType(ShowType.Modal);
	            formShowParameter.setCloseCallBack(new CloseCallBack(this,"bidt_process_bar_bill"));
	            this.getView().showForm(formShowParameter);
        }
	 }
}


4  在动态表单上加上插件逻辑

这里主要有两个逻辑

一是前端的逻辑,afterBindData,我们在加载页面时就会触发一次,所以在这里我们需要初始化前端展示,和唤起异步线程来处理任务,onProgress 进度条启动后,会定时触发这个事件,所以在这里我们可以通过批次号来查询任务处理进度。

二是后台的逻辑,这个是通过ProcessExecuteThread来写的。


import java.util.EventObject;
import java.util.Iterator;
import java.util.Map;

import kd.bos.context.RequestContext;
import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.form.control.Label;
import kd.bos.form.control.ProgressBar;
import kd.bos.form.control.events.ProgressEvent;
import kd.bos.form.control.events.ProgresssListener;
import kd.bos.form.plugin.AbstractFormPlugin;
import kd.bos.orm.query.QCP;
import kd.bos.orm.query.QFilter;
import kd.bos.servicehelper.BusinessDataServiceHelper;
import kd.bos.servicehelper.operation.SaveServiceHelper;
import kd.bos.threads.ThreadPools;

public class TestProcessPlugin  extends AbstractFormPlugin implements ProgresssListener{
	public void registerListener(EventObject e) {
		//注册进度条的监听
	     ProgressBar progressbar = (ProgressBar)getControl("bidt_progressbarap");
	     progressbar.addProgressListener(this);
	       
	}
	@Override
	public void onProgress(ProgressEvent paramProgressEvent) {
		//获取父页面传过来的批次号
		long batchNo=this.getView().getFormShowParameter().getCustomParam("batchNo");
		
		//查询已处理多少条
		QFilter filter = new QFilter("bidt_batch_no",QCP.equals,batchNo);
		QFilter statusFilter = new QFilter("billstatus",QCP.equals,"C");
		Map<Object, DynamicObject>   preProcessMap = BusinessDataServiceHelper.loadFromCache( "bidt_process_bar_bill",new QFilter[] {filter,statusFilter});
		int processedObjectSize= preProcessMap.size();
		
		//设置进度条百分比
		Label currentcountLab=this.getView().getControl("bidt_currentcount");
		currentcountLab.setText(processedObjectSize+"");
		int totalCount=this.getView().getFormShowParameter().getCustomParam("totalCount");
		int percent=(int)processedObjectSize*100/totalCount;//计算出已处理的百分比
		paramProgressEvent.setProgress(percent);
		
		//当处理大于100%,告知前端任务已完成
		if(percent>=100) {
			ProgressBar bar = (ProgressBar)getView().getControl("bidt_progressbarap");
			bar.stop();//可以停止刷新了
			this.getView().getParentView().showTipNotification("已全部处理完成",2000);
			this.getView().close();
		}
	}
	
	public void afterBindData(EventObject e) {
		//加载页面时的动作
		super.afterBindData(e);
		ProgressBar bar = (ProgressBar)getView().getControl("bidt_progressbarap");
		bar.start();//启动进度条,启动后,前端才能不断的发起onProgress请求
		long batchNo=this.getView().getFormShowParameter().getCustomParam("batchNo");//获取父类的传参
		
		//启动异步线程,来处理真实的业务逻辑
		ProcessExecuteThread exeThread = new ProcessExecuteThread(batchNo,RequestContext.get());
		ThreadPools.executeOnce("ProcessExecuteThread", exeThread);
		
		//改变前端的提示
		int totalCount=this.getView().getFormShowParameter().getCustomParam("totalCount");//获取父类的传参
		Label totalCountLab=this.getView().getControl("bidt_totalcount");
		totalCountLab.setText(totalCount+"");
		Label currentcountLab=this.getView().getControl("bidt_currentcount");
		currentcountLab.setText("0");
	}
	
}
//实际处理线程类
class ProcessExecuteThread implements Runnable{
	private long batchNo;
	private RequestContext rc;
	
	ProcessExecuteThread(long batchNo,RequestContext rc){
		this.batchNo =batchNo;
		this.rc = rc;
	}
	@Override
	public void run() {
		RequestContext.copyAndSet(this.rc);
		
		//根据批次号获取待处理的数据
		QFilter filter = new QFilter("bidt_batch_no",QCP.equals,batchNo);
		Map<Object, DynamicObject>   preProcessMap = BusinessDataServiceHelper.loadFromCache( "bidt_process_bar_bill",new QFilter[] {filter});
		Iterator iterator = preProcessMap.keySet().iterator();
		while (iterator.hasNext()){
		    Object next = iterator.next();
		    DynamicObject dynObject=preProcessMap.get(next);
		    //模拟具体业务逻辑,让线程休眠两秒
		    try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		   dynObject.set("billstatus", "C");
		   //这里为了简化案例,我们没有记录失败和异常的情况,正常开发是需要在这里记录
		   SaveServiceHelper.save(new DynamicObject[] { dynObject});
 		}
	}
}



四、效果图

开始处理的情况

image.png


处理过程中的情况

image.png



处理完成后,自动关闭弹窗页面,且数据也正常刷新为已审核的状态了

image.png


五、开发环境版本

COSMICV5.0.003.0

六、参考资料

【开发平台】指导手册

学习成长中心

https://vip.kingdee.com/article/224163738084505344?productLineId=29&isKnowledge=2




赞 7