java 基于服务做统一数据导出
1.我们经常需要做数据的导出,但是,不同的业务场景只是数据不同,导出动作却又很多共性,为此,我们可以采用动静分离思想,将动作抽取出来作为一个服务,数据依赖于动作,就能用一套代码处理一些公共的东西了。
2.思路:如果只是将动作作为util,那每个服务都需要依赖该动作,服务与动作有高度的耦合性,我的思路是将动作抽取到基础(公共)服务里,该服务只是做特定的动作,数据从别的服务过来,加工后处理成特定的流,供其他服务调用
3.代码实现:
base服务里动作:FileImportExportController.java
import io.swagger.annotations.Api; import lombok.AllArgsConstructor; import cn.togeek.service.FileImportExportService; import java.util.List; import java.util.Map; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("/ftp") @Api(tags = "统一文件导入导出") @AllArgsConstructor public class FileImportExportController { private final FileImportExportService fileImportExportService; @PostMapping("/export/xls") public byte[] exportFile(@RequestBody List<Map> data) throws Exception { return fileImportExportService.doExport(data); } @PostMapping("/import/xls") public List<Map> importFile(@RequestBody MultipartFile[] files) throws Exception { return fileImportExportService.doImport(files); } }
service实现:FileImportExportService.java
import cn.hutool.poi.excel.ExcelUtil; import cn.hutool.poi.excel.ExcelWriter; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.IntStream; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @Service public class FileImportExportService { public byte[] doExport(List<Map> data) throws IOException { ExcelWriter writer = ExcelUtil.getWriter(); writer.write(data); IntStream.range(0, data.size()+1).forEach(x -> { writer.setRowHeight(x, 30); }); IntStream.range(0, data.get(0).size()).forEach(j -> { writer.setColumnWidth(j, 40); }); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); writer.getWorkbook().write(outputStream); return outputStream.toByteArray(); } public List<Map> doImport(MultipartFile[] files) { return Collections.emptyList(); } }
依赖服务:SpotBaseDataFeign.java(fengin方式调用)
import cn.togeek.common.domain.ElectricityRetailer; import cn.togeek.common.domain.TradingUnit; import cn.togeek.common.domain.Unit; import cn.togeek.common.domain.UnitCost; import cn.togeek.domain.base.InstalledGenerationCapacityInfo; import cn.togeek.domain.base.UnitMarginalCost; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.core.io.Resource; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "spot-base-data", url = "${service.spot-base-data.url}", fallbackFactory = SpotBaseDataFeign.ApiBaseDataServiceFallbackFactory.class) @RequestMapping("/spot-base-data") public interface SpotBaseDataFeign { @PostMapping("/ftp/export/xls") byte[] exportFile(@RequestBody List<Map<Object,Object>> data); @Component static class ApiBaseDataServiceFallbackFactory extends FeignFallbackFactory<SpotBaseDataFeign> { @Override public SpotBaseDataFeign newHandler() { return new SpotBaseDataFeign() { @Override public byte[] exportFile(List<Map<Object,Object>> data) { return new byte[0]; } }; } } }
使用:
public void exportMonthlyTargetSummary(YearMonth month) { List<Map<Object, Object>> data = this.getMonthlyTargetSummary(month); ExportUtil.doExport(spotBaseDataFeign.exportFile(transTargetSummaryToExportFormat(data)),"中标量汇总" + month.toString() + ".xls"); }
util封装:
import cn.hutool.core.date.DateUnit; import cn.hutool.core.date.DateUtil; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.OutputStream; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.Date; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; public class ExportUtil { private static OutputStream getResponseOutputStreamWithFileName(String fileName) throws IOException { ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletResponse response = attr.getResponse(); response.setCharacterEncoding("utf-8"); // 设置编码格式 response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8")); OutputStream outputStream = response.getOutputStream(); return outputStream; } public static <T> void doExport(byte[] bytes, String fileName) { OutputStream outputStream = null; try { outputStream = ExportUtil.getResponseOutputStreamWithFileName(fileName); outputStream.write(bytes); outputStream.flush(); outputStream.close(); } catch(IOException e) { e.printStackTrace(); } } }
补充:由于项目需要,部分数据需要做合并单元格处理,遂对处理进行升级
import io.swagger.annotations.Api; import lombok.AllArgsConstructor; import cn.togeek.service.FileImportExportService; import java.util.List; import java.util.Map; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("/ftp") @Api(tags = "统一文件导入导出") @AllArgsConstructor public class FileImportExportController { private final FileImportExportService fileImportExportService; @PostMapping("/export/xls") public byte[] exportFile(@RequestBody Map<String,Object> originData) throws Exception { return fileImportExportService.doExport(originData); } @PostMapping("/import/xls") public List<Map> importFile(@RequestBody MultipartFile[] files) throws Exception { return fileImportExportService.doImport(files); } }
service:
import cn.hutool.poi.excel.ExcelUtil; import cn.hutool.poi.excel.ExcelWriter; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.IntStream; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @Service public class FileImportExportService { public byte[] doExport(Map<String,Object> originData) throws IOException { List<Object> data = ((List<Object>) originData.get("data")); ExcelWriter writer = ExcelUtil.getWriter(); int currentRow = 0,containerSize = 0; if(originData.containsKey("merge")) { List<Map> mergeInfo = (List<Map>) originData.get("merge"); for(Map map : mergeInfo) { Integer firstRow = (Integer) map.get("firstRow"); Integer lastRow = (Integer) map.get("lastRow"); Integer firstColumn = (Integer) map.get("firstColumn"); Integer lastColumn = (Integer) map.get("lastColumn"); Boolean isSetHeaderStyle = (Boolean) map.get("isSetHeaderStyle"); Object value = map.get("content"); if(firstRow == lastRow && firstColumn == lastColumn) { writer.writeCellValue(firstColumn,firstRow,value); } else { //isSetHeaderStyle = isSetHeaderStyle==null?false:isSetHeaderStyle; writer.merge(firstRow,lastRow,firstColumn,lastColumn,value,false); } currentRow = currentRow<lastRow?lastRow:currentRow; } writer.setCurrentRow(++currentRow); } if(data.get(0) instanceof List) { containerSize = ((List<?>) data.get(0)).size(); for(Object datum : data) { writer.writeRow(((List) datum)); } } else if (data.get(0) instanceof Map) { containerSize = ((Map) data.get(0)).size(); writer.write(data); } else { throw new IllegalArgumentException("无效果的参数"); } IntStream.range(0, data.size()+currentRow).forEach(x -> { writer.setRowHeight(x, 30); }); IntStream.range(0, containerSize).forEach(j -> { writer.setColumnWidth(j, 40); }); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); writer.getWorkbook().write(outputStream); return outputStream.toByteArray(); } public List<Map> doImport(MultipartFile[] files) { return Collections.emptyList(); } }
调用:
import cn.togeek.common.domain.ElectricityRetailer; import cn.togeek.common.domain.TradingUnit; import cn.togeek.common.domain.Unit; import cn.togeek.common.domain.UnitCost; import cn.togeek.domain.base.InstalledGenerationCapacityInfo; import cn.togeek.domain.base.UnitMarginalCost; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.core.io.Resource; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "spot-base-data", url = "${service.spot-base-data.url}", fallbackFactory = SpotBaseDataFeign.ApiBaseDataServiceFallbackFactory.class) @RequestMapping("/spot-base-data") public interface SpotBaseDataFeign { @PostMapping("/ftp/export/xls") byte[] exportFile(@RequestBody Map<String,Object> originData); @Component static class ApiBaseDataServiceFallbackFactory extends FeignFallbackFactory<SpotBaseDataFeign> { @Override public SpotBaseDataFeign newHandler() { return new SpotBaseDataFeign() { @Override public byte[] exportFile(Map<String,Object> originData) { return new byte[0]; } }; } } }
使用:
public void exportMonthlyTargetSummary(YearMonth month) { List<Map<Object, Object>> data = transTargetSummaryToExportFormat(this.getMonthlyTargetSummary(month)); HashMap<String, Object> originData = new HashMap<>(); originData.put("data",data.stream().map(x->x.values()).collect(Collectors.toList())); fillHeader(originData); ExportUtil.doExport(spotBaseDataFeign.exportFile(originData),"中标量汇总" + month.toString() + ".xls"); } private void fillHeader(HashMap<String, Object> data) { List<Map> list = new ArrayList<>(); list.add(new HashMap(){{ put("firstRow",0); put("lastRow",1); put("firstColumn",0); put("lastColumn",0); put("content","日期"); put("isSetHeaderStyle",true); }}); list.add(new HashMap(){{ put("firstRow",0); put("lastRow",0); put("firstColumn",1); put("lastColumn",8); put("content","广州公司"); put("isSetHeaderStyle",true); }}); list.add(new HashMap(){{ put("firstRow",0); put("lastRow",0); put("firstColumn",9); put("lastColumn",16); put("content","清远公司"); put("isSetHeaderStyle",true); }}); list.add(new HashMap(){{ put("firstRow",0); put("lastRow",0); put("firstColumn",17); put("lastColumn",24); put("content","韶关公司"); put("isSetHeaderStyle",true); }}); list.add(new HashMap(){{ put("firstRow",0); put("lastRow",0); put("firstColumn",25); put("lastColumn",32); put("content","坪石公司"); put("isSetHeaderStyle",true); }}); list.add(new HashMap(){{ put("firstRow",0); put("lastRow",0); put("firstColumn",33); put("lastColumn",40); put("content","区域合计"); put("isSetHeaderStyle",true); }}); IntStream.range(1,41).forEach(x->{ int mode = x % 8; String content = ""; switch(mode) { case 0: content = "实时偏差电量(万千瓦时)"; break; case 1: content = "中长期电量(万千瓦时)"; break; case 2: content = "日前中标电量(万千瓦时)"; break; case 3: content = "日前现货电量(万千瓦时)"; break; case 4: content = "全厂中长期覆盖率"; break; case 5: content = "日前全电量加权均价(厘/千瓦时)"; break; case 6: content = "实时电量(万千瓦时)"; break; case 7: content = "实时全电量加权均价(厘/千瓦时)"; break; default: break; } String finalContent = content; list.add(new HashMap(){{ put("firstRow",1); put("lastRow",1); put("firstColumn",x); put("lastColumn",x); put("content", finalContent); put("isSetHeaderStyle",true); }}); }); data.put("merge",list); }