上一篇文章分享了用户下载任务的提交与下载,以及系统中对任务的保存方式,这篇文章,主要分享excel处理部分。excel处理基本流程如下:
excel处理线程首先要获得生成excel表格的基本数据(其中不包括大的查询数据),保存到新创建的beansMap中;然后对于大的数据多次查询,将多次查询结果List统一保存到一个List中,最后保存到beansMap中,通过jxls生成excel报表。
public void run() { if(orderViewService != null && pager != null) { if(beansMap == null) beansMap = new HashMap<String, Object>(); // save query result list. List<OrderView> views = new LinkedList<OrderView>(); // temp pager Pager tmpPager = new Pager(); // copy pager to tmpPager try { BeanUtils.copyProperties(tmpPager, pager); tmpPager.setPage(1); } catch(InvocationTargetException e) { e.printStackTrace(); } catch(IllegalAccessException e) { e.printStackTrace(); } // query order view. tmpPager.setPage(1); tmpPager.setPageSize(SIZE_PER_PAGE); List<OrderView> orderViews = null; do { orderViews = orderViewService.find(tmpPager); tmpPager.setPage(tmpPager.getPage() + 1); views.addAll(orderViews); } while (ObjectUtil.isNotEmpty(orderViews) && orderViews.size() == 10); // order type beansMap.put("eshop", 0); beansMap.put("direct", 1); beansMap.put("distribution", 2); // shop list bean List<Eshop> shopList = eshopService.findAll(); Map<Integer, String> shopMap = new HashMap<Integer, String>(); for (Eshop shop : shopList) { shopMap.put(shop.getId(), shop.getName()); } shopMap.put(-1, "-线下-"); shopMap.put(null, "-空-"); // logistic name map bean Map<Integer, String> logisticMap = new HashMap<Integer, String>(); List<Logistic> logisticList = logisticService.findAll(); for (Logistic logistic : logisticList) { logisticMap.put(logistic.getId(), logistic.getName()); } // other beans beansMap.put("PayMode", Erp.PAYMODEMAP); beansMap.put("ShippingTypes", Erp.ShippingTypes.map); beansMap.put("OrderPayTypes", Erp.OrderPayTypes.map); beansMap.put("OrderStatus", Erp.OrderStatus.statusMap); beansMap.put("FLAGS", OrderViewServiceImpl.SELLERFLAGS); // current time. beansMap.put("CURRENT_TIME", new Date()); beansMap.put("data", views); // generate excel. ExportResult exportResult = exportConfigService.createExportFile(jobParams.getTemplateFileCode(), jobParams.getGenerateExcelFilePath(), beansMap); // update job parameter. generateExportFile(jobParams, exportResult.getWorkBook()); } } /** * write <code>Workbook</code> object to file. * @param jobParams job parameters * @see AsynchronyExportJobParams * @param workbook excel variable * @see Workbook * @return if success return true else return false */ private Boolean generateExportFile(AsynchronyExportJobParams jobParams, Workbook workbook) { // save excel into file. final String generateFilePrefix = Erp.contextPath + "/export/generate/"; Boolean res = true; try { OutputStream os = new BufferedOutputStream(new FileOutputStream(generateFilePrefix + jobParams.getGenerateExcelFilePath())); workbook.write(os); os.flush(); os.close(); } catch(IOException e) { res = false; } // update job parameters in DB. if(res) { jobParams = asynchronyExportService.load(jobParams.getId()); jobParams.setJobCurrentStatus(JobStatus.FINISHED); jobParams.setJobFinishTime(new Date()); jobParams.setIsDeleteExportedFile(false); if(!asynchronyExportService.saveOrUpdate(jobParams)) res = false; } return res; }
对于excel的管理,我采用newSingleThreadExecutor管理,基本Service类如下:
public class ProcessOrderExportService { private static ExecutorService exec = Executors.newSingleThreadExecutor(); /** * add a job into executor queue. */ public static void pushJob(Runnable job) { exec.execute(job); } /** * shutdown service. */ public static void shutdownService() { // interrupted all threads. exec.shutdown(); // wait for two seconds. try { TimeUnit.SECONDS.sleep(2); } catch(InterruptedException e) { e.printStackTrace(); } // shutdown all threads now. if(!exec.isShutdown()) exec.shutdownNow(); } /** * restart service. */ public static void restartService() { if(!exec.isTerminated()) exec.shutdownNow(); exec = Executors.newSingleThreadExecutor(); } }
这样这完成了excel导出任务的处理。对于不同的导出方式,可以创建不同的Runnable线程。线程中常见的问题就是数据的传递方式,可以采用二种方法,第一是采用数据Map<String, Object>在线程的构造方法中传递,例如:public ProcessOrderExportJob(Map<String, Object> paramMap)。第二种,线程中数据传递spring注入的方式,基本采用ApplicationContext.getBean(SomeClass.class)方式获取各种类。当然,与可以两种方式混合使用。我做异步的系统中采用SpringMVC框架,上面的线程参数传递,我采用混合的方式,线程构造方法如下:
/** * query conditions. */ private Pager pager; /** * beans map. */ private Map<String, Object> beansMap; /** * order view service. */ private OrderViewService orderViewService; /** * asynchrony export job parameters service. */ private AsynchronyExportJobParamsService asynchronyExportService; /** * export configuration service. */ private ExportConfigService exportConfigService; /** * eshop service. */ private EshopService eshopService; /** * logistic service. */ private LogisticService logisticService; /** * job parameters. */ private AsynchronyExportJobParams jobParams; /** * the constant variable of a size per page. */ private final static int SIZE_PER_PAGE = 10; public ProcessOrderExportJob(Pager pager, Map<String, Object> beansMap, AsynchronyExportJobParams jobParams) { this.pager = pager; this.beansMap = beansMap; this.jobParams = jobParams; orderViewService = Erp.context.getBean(OrderViewService.class); asynchronyExportService = Erp.context.getBean(AsynchronyExportJobParamsService.class); exportConfigService = Erp.context.getBean(ExportConfigService.class); eshopService = Erp.context.getBean(EshopService.class); logisticService = Erp.context.getBean(LogisticService.class); }
在线程的构造函数中,完成了参数的初始化。