本文介绍了在苍穹开发平台中,利用线程池处理后台任务时,通过前端进度条展示任务进度的实现方法。首先,明确进度条仅作为前端展示工具,不直接处理任务。其次,通过构造测试数据和弹窗进度条,以及在后端使用线程池异步处理任务,并实时更新进度条状态,展示了任务进度从开始到完成的整个过程。最后,展示了实现后的效果图,并提供了开发环境版本和参考资料。
关键词:进度条,线程池,任务进度
一、需求
在苍穹开发中,我们有时会用到线程池来处理一些后台任务,而这些任务往往我们不清楚已经处理到哪步了,这个时候想通过进度条来展示。
二、思路与方案
进度条是苍穹的一个前端组件,之前开发中,有人以为加了这个进度条就能自动转后台任务,这是不正确的。进度条就像电脑显示器,它并不会自动处理任务,它只是前台展示的作用,我们可以利用它来展示我们后台任务处理到多少百分比。它可以让我们的交互变的更加友好。
而实际的处理逻辑,我们可以通过线程池或者调度任务来处理,这里介绍下进度条如何来展示异步线程的处理。
为了方便演示,我们这次通过构造100条数据,然后异步线程来处理这100条数据从暂存状态变成审核状态,通过这种方式来展示进度条处理情况。
三、实现过程
1 构造测试单据及单据数据
我们在开发平台新建一个单据,为了方便后续对每批次的处理的数据不一样,我在上面加了一个批次号字段。
下一步就是构造简单的数据,通过下面这段代码,我就往单据里面灌了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秒刷新一次。
3 在单据列表上加上弹窗按钮
回到刚才我们创建单据,我在该单据的页面添加一个按钮,可以通过这个按钮弹窗显示有进度条的动态表单。
我在列表上加上插件,这个插件里面主要是两段逻辑,
在弹窗前把所有待处理的数据,记录批次号
弹窗-弹出进度条的动态表单
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}); } } }
四、效果图
开始处理的情况
处理过程中的情况
处理完成后,自动关闭弹窗页面,且数据也正常刷新为已审核的状态了
五、开发环境版本
COSMICV5.0.003.0
六、参考资料
https://vip.kingdee.com/article/224163738084505344?productLineId=29&isKnowledge=2
bidt_devtset-bidt_yanzheng-202 …(11.98KB)
推荐阅读