先说几个关键的点
pom.xml依赖
<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version>
</dependency>
<dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.12.2</version><exclusions><exclusion><groupId>poi</groupId><artifactId>poi</artifactId></exclusion><exclusion><groupId>poi-ooxml</groupId><artifactId>poi-ooxml</artifactId></exclusion></exclusions>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version><exclusions><exclusion><groupId>commons-io</groupId><artifactId>commons-io</artifactId></exclusion></exclusions>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.3</version><exclusions><exclusion><groupId>commons-io</groupId><artifactId>commons-io</artifactId></exclusion></exclusions>
</dependency>
横纵动态表格
首先读取你的数据,做好List<Map<String, Object>> list的数据,进行横表格纵表格动态填充
//XWPFTemplate模板启动所需配置LoopRowTableRenderPolicy行表格、LoopColumnTableRenderPolicy列表格
RenderPolicy policy1 = new LoopRowTableRenderPolicy(false);
Map<RenderPolicy, List<String>> polines1 = MapUtil.builder(policy1, Arrays.asList("rankScoreMapList")).map();
RenderPolicy policy2 = new LoopColumnTableRenderPolicy(false);
Map<RenderPolicy, List<String>> polines2 = MapUtil.builder(policy2, Arrays.asList("RL")).map();
List<Map<RenderPolicy, List<String>>> configList = Arrays.asList(polines1, polines2);
//渲染生成Word
String result = processWordTemplate(filename, map, scoreMapList, configList, rankScoreMapList, rankTicketTypeMapList);
return result;
读取配置并渲染数据
@SneakyThrows
public String processWordTemplate(String filename, Map<String, Object> replacements, List<Map<String, Object>> scoreMapList, List<Map<RenderPolicy, List<String>>> configList, List<Map<String, Object>> rankScoreMapList, List<Map<String, Object>> rankTicketTypeMapList) {byte[] fileBytes = upmsService.getFileBytes(filename);ByteArrayInputStream bis = new ByteArrayInputStream(fileBytes);//读取配置Configure config = Configure.createDefault();if (ObjectUtil.isNotEmpty(configList)) {for (Map<RenderPolicy, List<String>> map : configList) {for (Map.Entry<RenderPolicy, List<String>> entry : map.entrySet()) {RenderPolicy key = entry.getKey();List<String> value = entry.getValue();value.stream().forEach(val -> config.customPolicy(val, key));}}}//1. 渲染文本和表格数据replacementsXWPFTemplate template = XWPFTemplate.compile(bis, config).render(replacements);XWPFDocument document = template.getXWPFDocument();//2. 继续处理 -> 更改图表模板 -> 饼状图、折线图List<POIXMLDocumentPart> relations = document.getRelations();//.....详细代码见最下方。//完成操作后保存文档ByteArrayOutputStream bos = new ByteArrayOutputStream();document.write(bos);byte[] bytes = bos.toByteArray();PoitlIOUtils.closeQuietlyMulti(template, bos);//关闭流MultipartFile multipartFile = new MockMultipartFile(filename + ".docx", filename + ".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", bytes);R r = headersRemoteUpmsService.upload(multipartFile);if (r != null && r.isOk() && r.getData() != null) {Map<String, Object> data = (Map<String, Object>) r.getData();if (DataJudgeUtil.isNotBankMapKey(data, "url")) {return data.get("url").toString();}}return null;}
效果图如下:
注意1:横动态图表会自动获取该行的宽度,然后等分到每一栏中,我们需要将变量的长度给扩大,如果需要自定义宽度,请先移步到其他博客。
注意2:如果和我的一样为不标准的表格,需要放到该列的前一列中的任一单元格去,如:得分{{RL}}。放到测评项目单元格是不对的!!
更多可以参考官网:https://deepoove.com/poi-tl/#hack-loop-table
图表:
确保你的图表为这种excel表格,而不是普通的图片
图表填充(其实就是填充对应的excel表格,一行一行的填充数据)饼图为例
List<POIXMLDocumentPart> relations = document.getRelations();
for (POIXMLDocumentPart poixmlDocumentPart : relations) {if (poixmlDocumentPart instanceof XWPFChart) {XWPFChart chart = (XWPFChart) poixmlDocumentPart;XSSFSheet sheet = chart.getWorkbook().getSheetAt(0);XSSFCell cell = sheet.getRow(0).getCell(1);if (cell.getStringCellValue().equals("领导班子(A票)")) {// 过滤掉总分标签rankTicketTypeMapList = rankTicketTypeMapList.stream().filter(m -> !m.get("itemName").equals("总分")).collect(Collectors.toList());// 设置各指标得分的单元格for (int i = 0; i < rankTicketTypeMapList.size(); i++) {Row dataRow = sheet.getRow(i + 1);if (dataRow == null) {dataRow = sheet.createRow(i + 1);}//设置标签名Cell itemNameCell = dataRow.getCell(0);if (itemNameCell == null) {itemNameCell = dataRow.createCell(0);}itemNameCell.setCellValue(rankTicketTypeMapList.get(i).get("itemName").toString());itemNameCell.setCellType(CellType.STRING);// 设置得分单元格Cell scoreCellA = dataRow.getCell(1);if (scoreCellA == null) {scoreCellA = dataRow.createCell(1);}scoreCellA.setCellValue(Double.valueOf(rankTicketTypeMapList.get(i).get("score_A").toString()));scoreCellA.setCellType(CellType.NUMERIC);Cell scoreCellB = dataRow.getCell(2);if (scoreCellB == null) {scoreCellB = dataRow.createCell(2);}scoreCellB.setCellValue(Double.valueOf(rankTicketTypeMapList.get(i).get("score_B").toString()));scoreCellB.setCellType(CellType.NUMERIC);Cell scoreCellC = dataRow.getCell(3);if (scoreCellC == null) {scoreCellC = dataRow.createCell(3);}scoreCellC.setCellValue(Double.valueOf(rankTicketTypeMapList.get(i).get("score_C").toString()));scoreCellC.setCellType(CellType.NUMERIC);Cell scoreCellALL = dataRow.getCell(4);if (scoreCellALL == null) {scoreCellALL = dataRow.createCell(4);}scoreCellALL.setCellValue(Double.valueOf(rankTicketTypeMapList.get(i).get("score_ALL").toString()));scoreCellALL.setCellType(CellType.NUMERIC);}firstRow = 1;lastRow = rankTicketTypeMapList.size();firstCol = 1;lastCol = 4;// 为什么默认是八行?可能和建表图的时候添加了八行?删除多余的行for (int i = sheet.getLastRowNum(); i > rankTicketTypeMapList.size() + 1; i--) {sheet.removeRow(sheet.getRow(i));}}
}
填充完数据,需要刷新图表,才会把新数据应用到图表中:
s.replaceData(cat, val);chart.plot(chartData);
XDDFChartData chartData = chart.getChartSeries().get(0);
if (chartData instanceof XDDFPieChartData) {//判断类型为饼图XDDFChartData.Series s = chartData.getSeries().get(0);XDDFDataSource cat = XDDFDataSourcesFactory.fromStringCellRange(sheet,new CellRangeAddress(firstRow, lastRow, 0, 0));XDDFNumericalDataSource val = XDDFDataSourcesFactory.fromNumericCellRange(sheet,new CellRangeAddress(firstRow, lastRow, firstCol, lastCol));s.replaceData(cat, val);chart.plot(chartData);
}
折线图与柱状图的组合图需要分别获取并刷新
//应用数据后刷新
XDDFChartData chartData = chart.getChartSeries().get(0);XDDFChartData chartData0 = chart.getChartSeries().get(0);//XDDFBarChartData柱状系列数据
XDDFChartData chartData1 = chart.getChartSeries().get(1);//XDDFLineChartData折线系列数据XDDFDataSource cat = XDDFDataSourcesFactory.fromStringCellRange(sheet,new CellRangeAddress(firstRow, lastRow, 0, 0));
for (int i = 0; i < chartData0.getSeries().size(); i++) {XDDFChartData.Series s = chartData0.getSeries().get(i);XDDFNumericalDataSource val = XDDFDataSourcesFactory.fromNumericCellRange(sheet,new CellRangeAddress(firstRow, lastRow, firstCol + i, firstCol + i));s.replaceData(cat, val);
}
for (int i = 0; i < chartData1.getSeries().size(); i++) {XDDFChartData.Series s = chartData1.getSeries().get(i);XDDFNumericalDataSource val = XDDFDataSourcesFactory.fromNumericCellRange(sheet,new CellRangeAddress(firstRow, lastRow, firstCol + i + 2, firstCol + i + 2));s.replaceData(cat, val);
}
chart.plot(chartData0);
chart.plot(chartData1);
效果如图:
此组合图的编辑如下:(根据自己需要的设置,填充数据后也要换成自己的刷新方式)
全部代码如下:
前面一直都在获取数据,主要的代码去见processWordTemplate
折线图、饼状图、折线饼状组合图均见processWordTemplate方法
package com.grp.digitization.hr.common.rest.kpiticketfeedback;import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.plugin.table.LoopColumnTableRenderPolicy;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.deepoove.poi.policy.RenderPolicy;
import com.deepoove.poi.util.PoitlIOUtils;
import com.grp.digitization.admin.api.feign.RemoteUpmsService;
import com.grp.digitization.common.core.util.R;
import com.grp.digitization.common.security.annotation.Inner;
import com.grp.digitization.hr.feign.hr.HeadersRemoteUpmsService;
import com.grp.digitization.hr.utils.DataJudgeUtil;
import com.grp.digitization.platform.server.GrpServer;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xddf.usermodel.chart.*;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.util.*;
import java.util.stream.Collectors;@Slf4j
@RestController()
@RequestMapping("/KpiTicketFeedback")
@Api(value = "KPICadreFeedbackReport", tags = "中层干部考核任务->生成中层干部反馈报告WORD文件")
public class KpiTicketFeedbackController {@Resourceprivate RemoteUpmsService upmsService;@Autowiredprivate HeadersRemoteUpmsService headersRemoteUpmsService;// private static final String filename = "tohi-FeedbackReports_Final.docx";private static final String filename = "tohi-88b5591ba7064d43856e162d93adb9c2.docx";@SneakyThrows@RequestMapping("/exportFeedbackWord/{code}")@Inner(value = false)@ApiOperation(value = "exportFeedbackWord", notes = "生成中层干部反馈报告")public R exportFeedbackWord(@PathVariable String code) {//拿到该评分任务下的每个用户,为每个用户生成一份反馈报告String personSql = "SELECT obj.USERNAME,obj.NAME,obj.DENAME,su.US_TITLE,mv.ENDTIME,mv.TITLE,mv.YEAR\n" +"FROM `middlecadresobjflow` obj LEFT JOIN middlecadresreview mv ON obj.MIDDLECADRESREVIEWCODE = mv.MIDDLECADRESREVIEWCODE LEFT JOIN sys_user su ON obj.USERNAME = su.username \n" +"WHERE obj.MIDDLECADRESREVIEWCODE = '" + code + "'";List<Map<String, Object>> personMapList = GrpServer.getGrpServer().excuteQueryListMap(personSql);Map<String, String> resultMap = personMapList.stream().collect(Collectors.toMap(m -> m.get("USERNAME").toString(),m -> generatePersonWord(code, personMapList, m.get("USERNAME").toString())//调用方法获取生成的文件的URL));//写入数据库for (Map.Entry<String, String> entry : resultMap.entrySet()) {log.info("生成中层干部反馈报告key:{},value:{}", entry.getKey(), entry.getValue());String updateSql = "UPDATE middlecadresobjflow SET FEEDBACK_URL = '" + entry.getValue() + "'" +" WHERE MIDDLECADRESREVIEWCODE = '" + code + "' AND USERNAME = '" + entry.getKey() + "'";GrpServer.getGrpServer().excuteUpdate(updateSql);}return R.ok(resultMap);}@SneakyThrowsprivate String generatePersonWord(String code, List<Map<String, Object>> personMapList, String username) {if (StringUtils.isEmpty(username)) {return "";}Map<String, Object> map = new HashMap<>();//声明存放数据的mapOptional<Map<String, Object>> first = personMapList.stream().filter(m -> m.containsKey("USERNAME") && m.get("USERNAME").toString().equals(username)).findFirst();if (first.isPresent()) {Map<String, Object> tmp = first.get();map.put("name", DataJudgeUtil.isNotBankMapKey(tmp, "NAME") ? tmp.get("NAME").toString() : "");map.put("dept", DataJudgeUtil.isNotBankMapKey(tmp, "DENAME") ? tmp.get("DENAME").toString() : "");map.put("title", DataJudgeUtil.isNotBankMapKey(tmp, "US_TITLE") ? tmp.get("US_TITLE").toString() : "");map.put("date", DataJudgeUtil.isNotBankMapKey(tmp, "ENDTIME") ? tmp.get("ENDTIME").toString().split("T")[0] : "");map.put("theme", DataJudgeUtil.isNotBankMapKey(tmp, "TITLE") ? tmp.get("TITLE").toString() : "");map.put("year", DataJudgeUtil.isNotBankMapKey(tmp, "YEAR") ? tmp.get("YEAR").toString().split("-")[0] : "");}//获取票类型以及数量(有效)String ticketSql = "SELECT TICKETTYPE,WEIGHT,COUNT(*) AS NUM FROM `middlecadresscore` \n" +"WHERE MIDDLECADRESREVIEWCODE = '" + code + "' AND TICKETSTATE = '1' GROUP BY TICKETTYPE,WEIGHT";List<Map<String, Object>> ticketMapList = GrpServer.getGrpServer().excuteQueryListMap(ticketSql);long effective_SUM = ticketMapList.stream().map(m -> Integer.valueOf(m.get("NUM").toString())).mapToLong(Integer::intValue).sum();Double wight_SUM = ticketMapList.stream().map(m -> Double.valueOf(m.get("WEIGHT").toString())).mapToDouble(Double::doubleValue).sum();ticketMapList.forEach(m -> {int count = Integer.valueOf(m.get("NUM").toString());double percentage = (count * 100.0) / effective_SUM;String percentageStr = String.format("%.2f", percentage) + "%";map.put("EFF_" + m.get("TICKETTYPE").toString(), m.get("NUM").toString());map.put("PE_" + m.get("TICKETTYPE").toString(), percentageStr);map.put("WIGHT_" + m.get("TICKETTYPE").toString(), String.format("%.2f", Double.valueOf(m.get("WEIGHT").toString())) + "%");});map.put("EFF_SUM", String.valueOf(effective_SUM));map.put("WIGHT_SUM", String.format("%.2f", wight_SUM) + "%");//总票数String all_ticketSql = "SELECT TICKETTYPE,WEIGHT,COUNT(*) AS NUM FROM `middlecadresscore` \n" +"WHERE MIDDLECADRESREVIEWCODE = '" + code + "' GROUP BY TICKETTYPE,WEIGHT";List<Map<String, Object>> all_ticketMapList = GrpServer.getGrpServer().excuteQueryListMap(all_ticketSql);long all_SUM = all_ticketMapList.stream().map(m -> Integer.valueOf(m.get("NUM").toString())).mapToLong(Integer::intValue).sum();all_ticketMapList.forEach(m -> {int count = Integer.valueOf(m.get("NUM").toString());double percentage = (count * 100.0) / all_SUM;String percentageStr = String.format("%.2f", percentage) + "%";map.put("ALL_" + m.get("TICKETTYPE").toString(), m.get("NUM").toString());map.put("PA_" + m.get("TICKETTYPE").toString(), percentageStr);});map.put("ALL_SUM", String.valueOf(all_SUM));// 更改图表模板-> 饼状图//统计所有人的测评项目、综合得分String all_scoreSql = "SELECT OBJECTNO,itemCode,itemName,SUM(SCORE*WEIGHT)/100 as synthesisScore FROM \n" +"(SELECT mcp.itemCode,mcp.itemName,mcp.OBJECTNO,ms.ticketType,ms.WEIGHT,SUM(mcp.SCORE) / COUNT(*) AS SCORE FROM middlecadresscorecp mcp \n" +"LEFT JOIN middlecadresscore ms ON ms.MIDDLECADRESSCORECODE = mcp.MIDDLECADRESSCORECODE WHERE ms.MIDDLECADRESREVIEWCODE = '" + code + "' AND ms.TICKETSTATE = '1' \n" +"GROUP BY mcp.itemCode,mcp.itemName,mcp.OBJECTNO, ms.ticketType, ms.WEIGHT) tmp \n" +"GROUP BY itemCode,itemName,OBJECTNO \n";List<Map<String, Object>> all_scoreMapList = GrpServer.getGrpServer().excuteQueryListMap(all_scoreSql);//统计同组人所有人的测评项目的综合得分的最低分、平均分、最高分minScore、maxScore、avgScore// 先计算统计每个itemCode的信息Map<String, DoubleSummaryStatistics> statisticsMap = all_scoreMapList.stream().collect(Collectors.groupingBy(m -> m.get("itemCode").toString(),Collectors.mapping(m -> Double.valueOf(m.get("synthesisScore").toString()), Collectors.summarizingDouble(Double::doubleValue))));all_scoreMapList.forEach(m -> {String itemCode = m.get("itemCode").toString();DoubleSummaryStatistics stats = statisticsMap.get(itemCode);if (stats != null) {m.put("minScore", stats.getMin());m.put("maxScore", stats.getMax());m.put("avgScore", stats.getAverage());}});//计算个人的相关数据-折线图List<Map<String, Object>> scoreMapList = all_scoreMapList.stream().filter(m -> m.containsKey("OBJECTNO") && m.get("OBJECTNO").toString().equals(username)).collect(Collectors.toList());double score = scoreMapList.stream().map(m -> Double.valueOf(m.get("synthesisScore").toString())).mapToDouble(Double::doubleValue).sum();double averageScore = scoreMapList.stream().map(m -> Double.valueOf(m.get("synthesisScore").toString())).mapToDouble(Double::doubleValue).average().orElse(0.0);List<String> itemName_high = scoreMapList.stream().filter(m -> Double.valueOf(m.get("synthesisScore").toString()) >= averageScore).map(m -> m.get("itemName").toString()).collect(Collectors.toList());List<String> itemName_low = scoreMapList.stream().filter(m -> Double.valueOf(m.get("synthesisScore").toString()) < averageScore).map(m -> m.get("itemName").toString()).collect(Collectors.toList());map.put("SCORE", String.format("%.2f", score));map.put("AVG_SCORE", String.format("%.2f", averageScore));map.put("ITEM_HIGH", itemName_high.isEmpty() ? "无测评项目" : String.join("、", itemName_high));map.put("ITEM_LOW", itemName_low.isEmpty() ? "无测评项目" : String.join("、", itemName_low));//为all_scoreMapList每个OBJECTNO添加总分的一行数据List<String> userList = all_scoreMapList.stream().map(m -> m.get("OBJECTNO").toString()).distinct().collect(Collectors.toList());long count = userList.size();userList.forEach(u -> {double minScoreSum = all_scoreMapList.stream().filter(m -> m.get("OBJECTNO").toString().equals(u)).map(m -> Double.valueOf(m.get("minScore").toString())).mapToDouble(Double::doubleValue).sum();double avgScoreSum = all_scoreMapList.stream().filter(m -> m.get("OBJECTNO").toString().equals(u)).map(m -> Double.valueOf(m.get("avgScore").toString())).mapToDouble(Double::doubleValue).sum();double maxScoreSum = all_scoreMapList.stream().filter(m -> m.get("OBJECTNO").toString().equals(u)).map(m -> Double.valueOf(m.get("maxScore").toString())).mapToDouble(Double::doubleValue).sum();double synthesisScoreSum = all_scoreMapList.stream().filter(m -> m.get("OBJECTNO").toString().equals(u)).map(m -> Double.valueOf(m.get("synthesisScore").toString())).mapToDouble(Double::doubleValue).sum();HashMap<String, Object> tmpMap = new HashMap<>();tmpMap.put("OBJECTNO", u);tmpMap.put("itemCode", 0);//总分编号设置为0tmpMap.put("itemName", "总分");tmpMap.put("minScore", minScoreSum);tmpMap.put("avgScore", avgScoreSum);tmpMap.put("maxScore", maxScoreSum);tmpMap.put("synthesisScore", synthesisScoreSum);all_scoreMapList.add(tmpMap);});//个人排名渲染的listList<Map<String, Object>> rankScoreMapList = all_scoreMapList.stream().filter(m -> m.get("OBJECTNO").toString().equals(username)).collect(Collectors.toList());rankScoreMapList.forEach(m -> {long rank = all_scoreMapList.stream().filter(m2 -> m2.get("itemCode").equals(m.get("itemCode")) && Double.valueOf(m2.get("synthesisScore").toString()) > Double.valueOf(m.get("synthesisScore").toString())).count() + 1;m.put("rank", rank + "/" + count);});//score格式化为两位小数rankScoreMapList.forEach(m -> {m.put("minScore", String.format("%.2f", Double.valueOf(m.get("minScore").toString())));m.put("avgScore", String.format("%.2f", Double.valueOf(m.get("avgScore").toString())));m.put("maxScore", String.format("%.2f", Double.valueOf(m.get("maxScore").toString())));m.put("synthesisScore", String.format("%.2f", Double.valueOf(m.get("synthesisScore").toString())));});map.put("rankScoreMapList", rankScoreMapList);List<String> beaItemName = rankScoreMapList.stream().filter(m -> Math.abs(Double.valueOf(m.get("synthesisScore").toString()) - averageScore) < 0.25).map(m -> m.get("itemName").toString()).collect(Collectors.toList());map.put("BEA_ITEM_NAME", beaItemName.isEmpty() ? "无测评项目" : String.join("、", beaItemName));//统计同组人所有人的测评项目的A票score_A,B票score_B,C票score_C,(规则,票类型相同的加和求平均值)以及总分score_ALL(权重wight)String all_ticketTypeSql = "SELECT tmp.OBJECTNO,tmp.itemCode,tmp.itemName,SUM(CASE tmp.ticketType WHEN 'A' THEN tmp.score ELSE 0 END) AS score_A,SUM(CASE tmp.ticketType WHEN 'B' THEN tmp.score ELSE 0 END) AS score_B,SUM(CASE tmp.ticketType WHEN 'C' THEN tmp.score ELSE 0 END) AS score_C, \n" +"ROUND(SUM(CASE tmp.ticketType WHEN 'A' THEN tmp.score * tmp.WEIGHT / 100.0 ELSE 0 END) + SUM(CASE tmp.ticketType WHEN 'B' THEN tmp.score * tmp.WEIGHT / 100.0 ELSE 0 END) + SUM(CASE tmp.ticketType WHEN 'C' THEN tmp.score * tmp.WEIGHT / 100.0 ELSE 0 END), 2) AS score_ALL \n" +"FROM( SELECT mcp.itemCode,mcp.itemName,mcp.OBJECTNO,ms.ticketType,ms.WEIGHT,SUM(mcp.SCORE) / COUNT(*) AS SCORE FROM middlecadresscorecp mcp LEFT JOIN middlecadresscore ms ON ms.MIDDLECADRESSCORECODE = mcp.MIDDLECADRESSCORECODE\n" +" WHERE ms.MIDDLECADRESREVIEWCODE = '" + code + "' AND ms.TICKETSTATE = '1' GROUP BY mcp.itemCode, mcp.itemName, mcp.OBJECTNO, ms.ticketType, ms.WEIGHT\n" +") tmp GROUP BY tmp.itemCode, tmp.itemName, tmp.OBJECTNO\n";List<Map<String, Object>> all_ticketTypeMapList = GrpServer.getGrpServer().excuteQueryListMap(all_ticketTypeSql);//为all_ticketTypeMapList每个OBJECTNO添加总分的一行数据userList.forEach(u -> {double score_A = all_ticketTypeMapList.stream().filter(m -> m.get("OBJECTNO").toString().equals(u)).map(m -> Double.valueOf(m.get("score_A").toString())).mapToDouble(Double::doubleValue).sum();double score_B = all_ticketTypeMapList.stream().filter(m -> m.get("OBJECTNO").toString().equals(u)).map(m -> Double.valueOf(m.get("score_B").toString())).mapToDouble(Double::doubleValue).sum();double score_C = all_ticketTypeMapList.stream().filter(m -> m.get("OBJECTNO").toString().equals(u)).map(m -> Double.valueOf(m.get("score_C").toString())).mapToDouble(Double::doubleValue).sum();double score_ALL = all_ticketTypeMapList.stream().filter(m -> m.get("OBJECTNO").toString().equals(u)).map(m -> Double.valueOf(m.get("score_ALL").toString())).mapToDouble(Double::doubleValue).sum();HashMap<String, Object> tmpMap = new HashMap<>();tmpMap.put("OBJECTNO", u);tmpMap.put("itemCode", 0);//总分编号设置为0tmpMap.put("itemName", "总分");tmpMap.put("score_A", score_A);tmpMap.put("score_B", score_B);tmpMap.put("score_C", score_C);tmpMap.put("score_ALL", score_ALL);all_ticketTypeMapList.add(tmpMap);});//过滤成个人信息并添加排名rank信息List<Map<String, Object>> rankTicketTypeMapList = all_ticketTypeMapList.stream().filter(m -> m.get("OBJECTNO").toString().equals(username)).collect(Collectors.toList());rankTicketTypeMapList.forEach(m -> {long rank_A = all_ticketTypeMapList.stream().filter(m2 -> m2.get("itemCode").equals(m.get("itemCode")) && Double.valueOf(m2.get("score_A").toString()) > Double.valueOf(m.get("score_A").toString())).count() + 1;m.put("rank_A", rank_A + "/" + count);long rank_B = all_ticketTypeMapList.stream().filter(m2 -> m2.get("itemCode").equals(m.get("itemCode")) && Double.valueOf(m2.get("score_B").toString()) > Double.valueOf(m.get("score_B").toString())).count() + 1;m.put("rank_B", rank_B + "/" + count);long rank_C = all_ticketTypeMapList.stream().filter(m2 -> m2.get("itemCode").equals(m.get("itemCode")) && Double.valueOf(m2.get("score_C").toString()) > Double.valueOf(m.get("score_C").toString())).count() + 1;m.put("rank_C", rank_C + "/" + count);long rank_ALL = all_ticketTypeMapList.stream().filter(m2 -> m2.get("itemCode").equals(m.get("itemCode")) && Double.valueOf(m2.get("score_ALL").toString()) > Double.valueOf(m.get("score_ALL").toString())).count() + 1;m.put("rank_ALL", rank_ALL + "/" + count);});//score格式化为两位小数rankTicketTypeMapList.forEach(m -> {m.put("score_A", String.format("%.2f", Double.valueOf(m.get("score_A").toString())));m.put("score_B", String.format("%.2f", Double.valueOf(m.get("score_B").toString())));m.put("score_C", String.format("%.2f", Double.valueOf(m.get("score_C").toString())));m.put("score_ALL", String.format("%.2f", Double.valueOf(m.get("score_ALL").toString())));});
// map.put("rankTicketTypeMapList",rankTicketTypeMapList);map.put("RL", rankTicketTypeMapList);//提取每张票上的建议String suggestionSql = "SELECT SUGGESTION FROM `middlecadresscore` WHERE MIDDLECADRESREVIEWCODE = '" + code + "' AND TICKETSTATE = '1'";List<Map<String, Object>> suggestionMapList = GrpServer.getGrpServer().excuteQueryListMap(suggestionSql);if (suggestionMapList.size() >= 0) {List<String> suggestionList = suggestionMapList.stream().filter(m -> m != null && m.containsKey("SUGGESTION") && DataJudgeUtil.isNotBankMapKey(m, "SUGGESTION")).map(m -> m.get("SUGGESTION").toString()).collect(Collectors.toList());map.put("suggestion", String.join("\n", suggestionList));} else {map.put("suggestion", "无");}//XWPFTemplate模板启动所需配置LoopRowTableRenderPolicy行表格、LoopColumnTableRenderPolicy列表格RenderPolicy policy1 = new LoopRowTableRenderPolicy(false);Map<RenderPolicy, List<String>> polines1 = MapUtil.builder(policy1, Arrays.asList("rankScoreMapList")).map();RenderPolicy policy2 = new LoopColumnTableRenderPolicy(false);Map<RenderPolicy, List<String>> polines2 = MapUtil.builder(policy2, Arrays.asList("RL")).map();List<Map<RenderPolicy, List<String>>> configList = Arrays.asList(polines1, polines2);//渲染生成WordString result = processWordTemplate(filename, map, scoreMapList, configList, rankScoreMapList, rankTicketTypeMapList);return result;}@SneakyThrowspublic String processWordTemplate(String filename, Map<String, Object> replacements, List<Map<String, Object>> scoreMapList, List<Map<RenderPolicy, List<String>>> configList, List<Map<String, Object>> rankScoreMapList, List<Map<String, Object>> rankTicketTypeMapList) {byte[] fileBytes = upmsService.getFileBytes(filename);ByteArrayInputStream bis = new ByteArrayInputStream(fileBytes);//读取配置Configure config = Configure.createDefault();if (ObjectUtil.isNotEmpty(configList)) {for (Map<RenderPolicy, List<String>> map : configList) {for (Map.Entry<RenderPolicy, List<String>> entry : map.entrySet()) {RenderPolicy key = entry.getKey();List<String> value = entry.getValue();value.stream().forEach(val -> config.customPolicy(val, key));}}}//1. 渲染文本和表格数据replacementsXWPFTemplate template = XWPFTemplate.compile(bis, config).render(replacements);XWPFDocument document = template.getXWPFDocument();//2. 继续处理 -> 更改图表模板 -> 饼状图、折线图List<POIXMLDocumentPart> relations = document.getRelations();for (POIXMLDocumentPart poixmlDocumentPart : relations) {if (poixmlDocumentPart instanceof XWPFChart) {int firstRow = 0;int lastRow = 0;int firstCol = 0;int lastCol = 0;XWPFChart chart = (XWPFChart) poixmlDocumentPart;XSSFSheet sheet = chart.getWorkbook().getSheetAt(0);XSSFCell cell = sheet.getRow(0).getCell(1);//饼图if (cell.getStringCellValue().equals("主体人数")) {sheet.getRow(1).getCell(1).setCellValue(percentageStrToDouble(replacements.get("PE_A").toString()));sheet.getRow(2).getCell(1).setCellValue(percentageStrToDouble(replacements.get("PE_B").toString()));sheet.getRow(3).getCell(1).setCellValue(percentageStrToDouble(replacements.get("PE_C").toString()));sheet.getRow(1).getCell(1).setCellType(CellType.NUMERIC);sheet.getRow(2).getCell(1).setCellType(CellType.NUMERIC);sheet.getRow(3).getCell(1).setCellType(CellType.NUMERIC);firstRow = 1;lastRow = 3;firstCol = 1;lastCol = 1;}//饼图if (cell.getStringCellValue().equals("主体权重")) {sheet.getRow(1).getCell(1).setCellValue(percentageStrToDouble(replacements.get("WIGHT_A").toString()));sheet.getRow(2).getCell(1).setCellValue(percentageStrToDouble(replacements.get("WIGHT_B").toString()));sheet.getRow(3).getCell(1).setCellValue(percentageStrToDouble(replacements.get("WIGHT_C").toString()));sheet.getRow(1).getCell(1).setCellType(CellType.NUMERIC);sheet.getRow(2).getCell(1).setCellType(CellType.NUMERIC);sheet.getRow(3).getCell(1).setCellType(CellType.NUMERIC);firstRow = 1;lastRow = 3;firstCol = 1;lastCol = 1;}//折线图if (cell.getStringCellValue().equals("各指标得分")) {double averageScore = scoreMapList.stream().map(m -> Double.valueOf(m.get("synthesisScore").toString())).mapToDouble(Double::doubleValue).average().orElse(0.0);String formattedAverageScore = String.format("%.2f", averageScore);sheet.getRow(0).getCell(2).setCellValue("各指标平均分 " + formattedAverageScore);//TODO 设置标题后怎么刷新??// 设置各指标得分的单元格for (int i = 0; i < scoreMapList.size(); i++) {Row dataRow = sheet.getRow(i + 1);if (dataRow == null) {dataRow = sheet.createRow(i + 1);}//设置标签名Cell itemNameCell = dataRow.getCell(0);if (itemNameCell == null) {itemNameCell = dataRow.createCell(0);}itemNameCell.setCellValue(scoreMapList.get(i).get("itemName").toString());itemNameCell.setCellType(CellType.STRING);// 设置得分单元格Cell scoreCell = dataRow.getCell(1);if (scoreCell == null) {scoreCell = dataRow.createCell(1);}scoreCell.setCellValue(Double.valueOf(scoreMapList.get(i).get("synthesisScore").toString()));scoreCell.setCellType(CellType.NUMERIC);// 设置平均分单元格Cell avgCell = dataRow.getCell(2);if (avgCell == null) {avgCell = dataRow.createCell(2);}avgCell.setCellValue(Double.valueOf(formattedAverageScore));avgCell.setCellType(CellType.NUMERIC);}firstRow = 1;lastRow = scoreMapList.size();firstCol = 1;lastCol = 2;// 为什么默认是八行?可能和建表图的时候添加了八行?删除多余的行for (int i = sheet.getLastRowNum(); i > scoreMapList.size() + 1; i--) {sheet.removeRow(sheet.getRow(i));}}//折线图与柱状图的组合图if (cell.getStringCellValue().equals("同组人员得分范围下限")) {// 过滤掉总分标签rankScoreMapList = rankScoreMapList.stream().filter(m -> !m.get("itemName").equals("总分")).collect(Collectors.toList());// 设置各指标得分的单元格for (int i = 0; i < rankScoreMapList.size(); i++) {Row dataRow = sheet.getRow(i + 1);if (dataRow == null) {dataRow = sheet.createRow(i + 1);}//设置标签名Cell itemNameCell = dataRow.getCell(0);if (itemNameCell == null) {itemNameCell = dataRow.createCell(0);}itemNameCell.setCellValue(rankScoreMapList.get(i).get("itemName").toString());itemNameCell.setCellType(CellType.STRING);// 设置同组人员得分下限Cell scoreLowCell = dataRow.getCell(1);if (scoreLowCell == null) {scoreLowCell = dataRow.createCell(1);}scoreLowCell.setCellValue(Double.valueOf(rankScoreMapList.get(i).get("minScore").toString()));scoreLowCell.setCellType(CellType.NUMERIC);// 设置同组人员得分范围Cell scoreRangeCell = dataRow.getCell(2);if (scoreRangeCell == null) {scoreRangeCell = dataRow.createCell(2);}scoreRangeCell.setCellValue(Double.valueOf(rankScoreMapList.get(i).get("maxScore").toString()) - Double.valueOf(rankScoreMapList.get(i).get("minScore").toString()));scoreRangeCell.setCellType(CellType.NUMERIC);// 设置平均分单元格Cell avgCell = dataRow.getCell(3);if (avgCell == null) {avgCell = dataRow.createCell(3);}avgCell.setCellValue(Double.valueOf(rankScoreMapList.get(i).get("avgScore").toString()));avgCell.setCellType(CellType.NUMERIC);// 设置平均分单元格Cell synthesisScoreCell = dataRow.getCell(4);if (synthesisScoreCell == null) {synthesisScoreCell = dataRow.createCell(4);}synthesisScoreCell.setCellValue(Double.valueOf(rankScoreMapList.get(i).get("synthesisScore").toString()));synthesisScoreCell.setCellType(CellType.NUMERIC);}firstRow = 1;lastRow = rankScoreMapList.size();firstCol = 1;lastCol = 4;//删除多余的行for (int i = sheet.getLastRowNum(); i > rankScoreMapList.size() + 1; i--) {sheet.removeRow(sheet.getRow(i));}}//折线图if (cell.getStringCellValue().equals("领导班子(A票)")) {// 过滤掉总分标签rankTicketTypeMapList = rankTicketTypeMapList.stream().filter(m -> !m.get("itemName").equals("总分")).collect(Collectors.toList());// 设置各指标得分的单元格for (int i = 0; i < rankTicketTypeMapList.size(); i++) {Row dataRow = sheet.getRow(i + 1);if (dataRow == null) {dataRow = sheet.createRow(i + 1);}//设置标签名Cell itemNameCell = dataRow.getCell(0);if (itemNameCell == null) {itemNameCell = dataRow.createCell(0);}itemNameCell.setCellValue(rankTicketTypeMapList.get(i).get("itemName").toString());itemNameCell.setCellType(CellType.STRING);// 设置得分单元格Cell scoreCellA = dataRow.getCell(1);if (scoreCellA == null) {scoreCellA = dataRow.createCell(1);}scoreCellA.setCellValue(Double.valueOf(rankTicketTypeMapList.get(i).get("score_A").toString()));scoreCellA.setCellType(CellType.NUMERIC);Cell scoreCellB = dataRow.getCell(2);if (scoreCellB == null) {scoreCellB = dataRow.createCell(2);}scoreCellB.setCellValue(Double.valueOf(rankTicketTypeMapList.get(i).get("score_B").toString()));scoreCellB.setCellType(CellType.NUMERIC);Cell scoreCellC = dataRow.getCell(3);if (scoreCellC == null) {scoreCellC = dataRow.createCell(3);}scoreCellC.setCellValue(Double.valueOf(rankTicketTypeMapList.get(i).get("score_C").toString()));scoreCellC.setCellType(CellType.NUMERIC);Cell scoreCellALL = dataRow.getCell(4);if (scoreCellALL == null) {scoreCellALL = dataRow.createCell(4);}scoreCellALL.setCellValue(Double.valueOf(rankTicketTypeMapList.get(i).get("score_ALL").toString()));scoreCellALL.setCellType(CellType.NUMERIC);}firstRow = 1;lastRow = rankTicketTypeMapList.size();firstCol = 1;lastCol = 4;// 为什么默认是八行?可能和建表图的时候添加了八行?删除多余的行for (int i = sheet.getLastRowNum(); i > rankTicketTypeMapList.size() + 1; i--) {sheet.removeRow(sheet.getRow(i));}}//应用数据后刷新XDDFChartData chartData = chart.getChartSeries().get(0);if (chartData instanceof XDDFPieChartData) {//饼图XDDFChartData.Series s = chartData.getSeries().get(0);XDDFDataSource cat = XDDFDataSourcesFactory.fromStringCellRange(sheet,new CellRangeAddress(firstRow, lastRow, 0, 0));XDDFNumericalDataSource val = XDDFDataSourcesFactory.fromNumericCellRange(sheet,new CellRangeAddress(firstRow, lastRow, firstCol, lastCol));s.replaceData(cat, val);chart.plot(chartData);} else if (chartData instanceof XDDFLineChartData) {//折线图XDDFDataSource cat = XDDFDataSourcesFactory.fromStringCellRange(sheet,new CellRangeAddress(firstRow, lastRow, 0, 0));for (int i = 0; i < chartData.getSeries().size(); i++) {XDDFChartData.Series s = chartData.getSeries().get(i);XDDFNumericalDataSource val = XDDFDataSourcesFactory.fromNumericCellRange(sheet,new CellRangeAddress(firstRow, lastRow, firstCol + i, firstCol + i));s.replaceData(cat, val);}chart.plot(chartData);} else {//折线图与柱状图的组合图XDDFChartData chartData0 = chart.getChartSeries().get(0);//XDDFBarChartData柱状系列数据XDDFChartData chartData1 = chart.getChartSeries().get(1);//XDDFLineChartData折线系列数据XDDFDataSource cat = XDDFDataSourcesFactory.fromStringCellRange(sheet,new CellRangeAddress(firstRow, lastRow, 0, 0));for (int i = 0; i < chartData0.getSeries().size(); i++) {XDDFChartData.Series s = chartData0.getSeries().get(i);XDDFNumericalDataSource val = XDDFDataSourcesFactory.fromNumericCellRange(sheet,new CellRangeAddress(firstRow, lastRow, firstCol + i, firstCol + i));s.replaceData(cat, val);}for (int i = 0; i < chartData1.getSeries().size(); i++) {XDDFChartData.Series s = chartData1.getSeries().get(i);XDDFNumericalDataSource val = XDDFDataSourcesFactory.fromNumericCellRange(sheet,new CellRangeAddress(firstRow, lastRow, firstCol + i + 2, firstCol + i + 2));s.replaceData(cat, val);}chart.plot(chartData0);chart.plot(chartData1);}}}//完成操作后保存文档ByteArrayOutputStream bos = new ByteArrayOutputStream();document.write(bos);byte[] bytes = bos.toByteArray();PoitlIOUtils.closeQuietlyMulti(template, bos);//关闭流MultipartFile multipartFile = new MockMultipartFile(filename + ".docx", filename + ".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", bytes);R r = headersRemoteUpmsService.upload(multipartFile);if (r != null && r.isOk() && r.getData() != null) {Map<String, Object> data = (Map<String, Object>) r.getData();if (DataJudgeUtil.isNotBankMapKey(data, "url")) {return data.get("url").toString();}}return null;}public Double percentageStrToDouble(String percentageStr) {String cleanedStr = percentageStr.replace("%", "");double value = Double.parseDouble(cleanedStr);double result = value / 100;return result;}
}