查询统计导出execl方法

This commit is contained in:
tangw 2026-09-16 16:16:20 +08:00
parent d54e41636a
commit f2b4aac602
7 changed files with 395 additions and 45 deletions

View File

@ -73,6 +73,7 @@
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>

View File

@ -1,43 +1,53 @@
package com.czlis.interfaceCommon.utils;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.handler.RowWriteHandler;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
public class EasyExcelExportUtil {
/** 列配置:字段key -> 列配置 */
private static final DateTimeFormatter DATE_FORMAT =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
/** 列配置 */
public static class ColumnConfig {
private String name; // 表头列名
private Integer width; // 前端列宽(px),null 或 0 不设置
private Integer width; // 前端列宽(px)
private Boolean sum; // true = 该列需要合计(生成合计行时求和)
public ColumnConfig() {}
public ColumnConfig(String name) { this.name = name; }
public ColumnConfig(String name, Integer width) { this.name = name; this.width = width; }
public ColumnConfig(String name, Integer width, Boolean sum) {
this.name = name; this.width = width; this.sum = sum;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getWidth() { return width; }
public void setWidth(Integer width) { this.width = width; }
public Boolean getSum() { return sum; }
public void setSum(Boolean sum) { this.sum = sum; }
}
/** 手动列宽 Handler(3.3.4 无 setColumnWidthMap,用此方案) */
/** 手动列宽 Handler */
public static class ColumnWidthHandler implements SheetWriteHandler {
private final Map<Integer, Integer> columnWidthMap;
public ColumnWidthHandler(Map<Integer, Integer> columnWidthMap) {
this.columnWidthMap = columnWidthMap == null ? Collections.emptyMap() : columnWidthMap;
}
@Override
public void beforeSheetCreate(WriteWorkbookHolder w, WriteSheetHolder s) {
}
public void beforeSheetCreate(WriteWorkbookHolder w, WriteSheetHolder s) {}
@Override
public void afterSheetCreate(WriteWorkbookHolder w, WriteSheetHolder s) {
Sheet sheet = s.getSheet();
@ -47,21 +57,49 @@ public class EasyExcelExportUtil {
}
}
/** 合计行蓝色字体 Handler */
public static class SummaryRowStyleHandler implements RowWriteHandler {
private final int summaryRowIndex;
public SummaryRowStyleHandler(int summaryRowIndex) {
this.summaryRowIndex = summaryRowIndex;
}
@Override
public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
Row row, Integer relativeRowIndex, Boolean isHead) {
if (Boolean.TRUE.equals(isHead)) {
return;
}
if (row.getRowNum() != summaryRowIndex) {
return;
}
Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
Font font = workbook.createFont();
font.setColor(IndexedColors.BLUE.getIndex()); // 蓝色
font.setBold(true); // 加粗(可选)
CellStyle style = workbook.createCellStyle();
style.setFont(font);
for (int i = 0; i < row.getLastCellNum(); i++) {
Cell cell = row.getCell(i);
if (cell == null) {
cell = row.createCell(i);
}
cell.setCellStyle(style);
}
}
}
/**
* 导出 List<Map<String, Object>> 到 Excel
*
* @param dataList 数据集合(字段key -> 值)
* @param columnMap 列配置:字段key -> ColumnConfig(LinkedHashMap 控制列顺序)
* @param dictCache 字典翻译:字段key -> {代码值: 中文标签};不需要翻译的字段不放入,或传空 Map
* @param columnMap 列配置:字段key -> ColumnConfig(LinkedHashMap 控制列顺序)
* 其中 sum=true 的列会自动生成合计行(第一列"合计",蓝色字体)
* @param dictCache 字典翻译:字段key -> {代码值: 中文标签},可传空 Map
*/
public static void exportListMap(HttpServletResponse response,
String fileName,
String sheetName,
String fileName, String sheetName,
List<Map<String, Object>> dataList,
Map<String, ColumnConfig> columnMap,
Map<String, Map<String, String>> dictCache) throws IOException {
// 1. 响应头(中文文件名 URL 编码,防乱码)
// 1. 响应头
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String encodedName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
@ -90,7 +128,7 @@ public class EasyExcelExportUtil {
colIndex++;
}
// 3. 数据行转换(按入参 dictCache 翻译 + 按列顺序取值)
// 3. 数据行转换(日期格式化 + 字典翻译)
Map<String, Map<String, String>> dict = dictCache == null ? Collections.emptyMap() : dictCache;
List<List<Object>> rows = new ArrayList<>(dataList == null ? 0 : dataList.size());
if (dataList != null) {
@ -98,10 +136,13 @@ public class EasyExcelExportUtil {
List<Object> row = new ArrayList<>(fieldKeys.size());
for (String key : fieldKeys) {
Object val = map.get(key);
if (val instanceof java.util.Date) {
val = DATE_FORMAT.format(((java.util.Date) val).toInstant());
}
Map<String, String> codeLabel = dict.get(key);
if (val != null && codeLabel != null) {
String label = codeLabel.get(String.valueOf(val));
val = label != null ? label : val; // 字典无对应项回显原值
String label = codeLabel.get(String.valueOf(val).trim());
val = label != null ? label : val;
}
row.add(val);
}
@ -109,12 +150,53 @@ public class EasyExcelExportUtil {
}
}
// 4. 写入:列宽用 Handler 实现
EasyExcel.write(response.getOutputStream())
.registerWriteHandler(new ColumnWidthHandler(columnWidthMap))
.head(head)
.sheet(sheetName)
.doWrite(rows);
// 4. 合计行:找出 sum=true 的列,单次遍历累加
Map<Integer, BigDecimal> sumMap = new HashMap<>();
for (int i = 0; i < fieldKeys.size(); i++) {
if (Boolean.TRUE.equals(columnMap.get(fieldKeys.get(i)).getSum())) {
sumMap.put(i, BigDecimal.ZERO);
}
}
boolean hasSummaryRow = false;
if (!sumMap.isEmpty()) {
if (dataList != null) {
for (Map<String, Object> map : dataList) {
for (Map.Entry<Integer, BigDecimal> e : sumMap.entrySet()) {
Object v = map.get(fieldKeys.get(e.getKey()));
if (v == null) {
continue;
}
try {
e.setValue(e.getValue().add(new BigDecimal(String.valueOf(v).trim())));
} catch (NumberFormatException ignored) {
// 非数值不参与合计
}
}
}
}
// 组装合计行:第一列"合计",sum 列填求和值,其余留空
List<Object> summaryRow = new ArrayList<>(fieldKeys.size());
for (int i = 0; i < fieldKeys.size(); i++) {
if (i == 0) {
summaryRow.add("合计");
} else if (sumMap.containsKey(i)) {
summaryRow.add(sumMap.get(i).stripTrailingZeros().toPlainString());
} else {
summaryRow.add("");
}
}
rows.add(summaryRow);
hasSummaryRow = true;
}
// 5. 写入:列宽 Handler + 合计行蓝色
ExcelWriterBuilder builder = EasyExcel.write(response.getOutputStream())
.registerWriteHandler(new ColumnWidthHandler(columnWidthMap));
if (hasSummaryRow) {
// 合计行绝对行号 = 表头1行 + rows.size() - 1 = rows.size()
builder.registerWriteHandler(new SummaryRowStyleHandler(rows.size()));
}
builder.head(head).sheet(sheetName).doWrite(rows);
}
/** 前端列宽(px) -> Excel 列宽(1/256 字符):像素 ≈ 字符数*7 + 5 */
@ -126,3 +208,4 @@ public class EasyExcelExportUtil {
return chars * 256;
}
}

View File

@ -0,0 +1,216 @@
package com.czlis.interfaceCommon.utils;
import com.itextpdf.io.font.FontProgram;
import com.itextpdf.io.font.FontProgramFactory;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.kernel.colors.Color;
import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.colors.DeviceRgb;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.properties.TextAlignment;
import com.itextpdf.layout.properties.UnitValue;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
public class PdfExportUtil {
private static final DateTimeFormatter DATE_FORMAT =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
/**
* 静态缓存 FontProgram:字体程序与文档无关,可全局复用;
* 注意:不能缓存 PdfFont,PdfFont 与 PdfDocument 绑定,跨请求复用会报
* "Pdf indirect object belongs to other PDF document"
*/
private static volatile FontProgram baseFontProgram;
private static FontProgram getBaseFontProgram() throws IOException {
if (baseFontProgram == null) {
synchronized (PdfExportUtil.class) {
if (baseFontProgram == null) {
String[] fontPaths = {
"C:/Windows/Fonts/simsun.ttc,0", // 宋体(,0 是 TTC 集合索引)
"C:/Windows/Fonts/msyh.ttc,0", // 微软雅黑
"C:/Windows/Fonts/simhei.ttf", // 黑体
"/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc,0" // Linux 备选
};
IOException last = null;
for (String path : fontPaths) {
try {
baseFontProgram = FontProgramFactory.createFont(path);
break;
} catch (IOException e) {
last = e;
}
}
if (baseFontProgram == null) {
throw last == null ? new IOException("未找到可用中文字体") : last;
}
}
}
}
return baseFontProgram;
}
/** 单元格文字:PdfFont 由调用方按当前文档传入 */
private static Paragraph text(PdfFont font, String content, float size, boolean bold, Color color) {
Paragraph p = new Paragraph(content == null ? "" : content)
.setFont(font)
.setFontSize(size)
.setFontColor(color);
if (bold) {
p.setBold();
}
return p;
}
/**
* 生成 PDF 报表(打印用)
* @param title 报表标题,可传 null
* @param columnMap 列配置(sum=true 的列生成蓝色合计行)
* @param dictCache 字典翻译:字段key -> {代码值: 中文标签}
*/
public static void printPdf(HttpServletResponse response,
String fileName,
String title,
List<Map<String, Object>> dataList,
Map<String, EasyExcelExportUtil.ColumnConfig> columnMap,
Map<String, Map<String, String>> dictCache) throws Exception {
// 1. 响应头
response.setContentType("application/pdf");
response.setCharacterEncoding("utf-8");
String encodedName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "inline;filename*=utf-8''" + encodedName + ".pdf");
// 2. 文档:横向 A4
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(response.getOutputStream()));
pdfDocument.setDefaultPageSize(PageSize.A4.rotate());
Document document = new Document(pdfDocument);
// ★ 每个文档新建 PdfFont(从缓存的 FontProgram 创建),禁止静态缓存 PdfFont
PdfFont font = PdfFontFactory.createFont(getBaseFontProgram(), PdfEncodings.IDENTITY_H,
PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
// 3. 标题
if (title != null && !title.isEmpty()) {
document.add(new Paragraph(title)
.setFont(font).setFontSize(14).setBold()
.setTextAlignment(TextAlignment.CENTER));
}
// 4. 列配置:点宽度(pt),彻底绕开百分比求和校验
List<String> fieldKeys = new ArrayList<>();
float[] pointWidths = new float[columnMap.size()];
int i = 0;
for (Map.Entry<String, EasyExcelExportUtil.ColumnConfig> entry : columnMap.entrySet()) {
fieldKeys.add(entry.getKey());
// 前端 px -> pt(96dpi 下 1px = 0.75pt);未配宽度的列给默认 100
float px = entry.getValue().getWidth() == null || entry.getValue().getWidth() <= 0
? 100 : entry.getValue().getWidth();
pointWidths[i++] = px * 0.75f;
}
// 总宽超过页面可用宽度时按比例缩放(保留列宽比例)
float usable = PageSize.A4.rotate().getWidth() - document.getLeftMargin() - document.getRightMargin();
float total = 0;
for (float w : pointWidths) {
total += w;
}
if (total > usable) {
float scale = usable / total;
for (int j = 0; j < pointWidths.length; j++) {
pointWidths[j] *= scale;
}
total = usable;
}
// 点宽度表格:不再 useAllAvailableWidth,宽度显式指定
Table table = new Table(pointWidths);
table.setWidth(UnitValue.createPointValue(total));
// 5. 表头
for (Map.Entry<String, EasyExcelExportUtil.ColumnConfig> entry : columnMap.entrySet()) {
Cell headerCell = new Cell()
.add(text(font, entry.getValue().getName(), 10, true, ColorConstants.BLACK))
.setTextAlignment(TextAlignment.CENTER)
.setBackgroundColor(new DeviceRgb(0xE4, 0xE3, 0xDD));
table.addHeaderCell(headerCell);
}
// 6. 数据行(日期格式化 + 字典翻译)
Map<String, Map<String, String>> dict = dictCache == null ? Collections.emptyMap() : dictCache;
if (dataList == null || dataList.isEmpty()) {
Cell empty = new Cell(1, columnMap.size())
.add(text(font, "无数据", 10, false, ColorConstants.BLACK))
.setTextAlignment(TextAlignment.CENTER);
table.addCell(empty);
} else {
for (Map<String, Object> map : dataList) {
for (String key : fieldKeys) {
Object val = map.get(key);
if (val instanceof java.util.Date) {
val = DATE_FORMAT.format(((java.util.Date) val).toInstant());
}
Map<String, String> codeLabel = dict.get(key);
if (val != null && codeLabel != null) {
String label = codeLabel.get(String.valueOf(val).trim());
val = label != null ? label : val;
}
table.addCell(new Cell()
.add(text(font, val == null ? "" : String.valueOf(val), 9, false, ColorConstants.BLACK)));
}
}
// 7. 合计行(蓝色加粗)
Map<Integer, BigDecimal> sumMap = new HashMap<>();
for (int k = 0; k < fieldKeys.size(); k++) {
if (Boolean.TRUE.equals(columnMap.get(fieldKeys.get(k)).getSum())) {
sumMap.put(k, BigDecimal.ZERO);
}
}
if (!sumMap.isEmpty()) {
for (Map<String, Object> map : dataList) {
for (Map.Entry<Integer, BigDecimal> e : sumMap.entrySet()) {
Object v = map.get(fieldKeys.get(e.getKey()));
if (v == null) {
continue;
}
try {
e.setValue(e.getValue().add(new BigDecimal(String.valueOf(v).trim())));
} catch (NumberFormatException ignored) {
}
}
}
for (int k = 0; k < fieldKeys.size(); k++) {
String v;
if (k == 0) {
v = "合计";
} else if (sumMap.containsKey(k)) {
v = sumMap.get(k).stripTrailingZeros().toPlainString();
} else {
v = "";
}
table.addCell(new Cell().add(text(font, v, 9, true, ColorConstants.BLUE)));
}
}
}
document.add(table);
document.close();
}
}

View File

@ -1,10 +1,12 @@
package com.czlis.liswork.controller.stats;
import com.czlis.common.core.domain.entity.lis.ComStatsParameter;
import com.czlis.common.core.domain.entity.lis.ComStatsTemplate;
import com.czlis.interfaceCommon.utils.LisUtil;
import com.czlis.interfaceCommon.utils.EasyExcelExportUtil;
import com.czlis.common.core.controller.BaseController;
import com.czlis.common.core.domain.Result;
import com.czlis.interfaceCommon.utils.PdfExportUtil;
import com.czlis.liswork.service.StatsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@ -34,10 +36,10 @@ public class StatsController extends BaseController{
return statsService.query(list);
}
@ApiOperation("导出execl")
@PostMapping("/export")
public void export(@RequestBody Map<String, Object> params, HttpServletResponse response) throws IOException {
@PostMapping("/exportExcel")
public void export(@RequestBody Map<String, Object> list, HttpServletResponse response) throws IOException {
Result res = statsService.query(params);
Result res = statsService.query(list);
if (res == null) return;
if (!"0".equals(res.getCode())) return;
@ -50,7 +52,7 @@ public class StatsController extends BaseController{
}
// 列配置 + 字典缓存(入参引入)
List<ComStatsParameter> columnList = LisUtil.mapToEntityList(params, "Column", ComStatsParameter.class);
List<ComStatsParameter> columnList = LisUtil.mapToEntityList(list, "Column", ComStatsParameter.class);
Map<String, EasyExcelExportUtil.ColumnConfig> columnMap = new LinkedHashMap<>();
Map<String, Map<String, String>> dictCache = new HashMap<>();
if (columnList != null) {
@ -64,7 +66,8 @@ public class StatsController extends BaseController{
String name = column.getParamName();
if (name == null) name = "";
Integer width = column.getParamWidth() == null ? null : column.getParamWidth().intValue();
columnMap.put(code, new EasyExcelExportUtil.ColumnConfig(name, width));
boolean isSum ="6".equals(column.getParamType());
columnMap.put(code, new EasyExcelExportUtil.ColumnConfig(name, width,isSum));
String zdlb = column.getRemark();
if (zdlb != null && !zdlb.trim().isEmpty()) {
dictCache.put(code, statsService.getDictCache(zdlb));
@ -78,8 +81,53 @@ public class StatsController extends BaseController{
}
@ApiOperation("打印")
@PostMapping(value = "/print")
public Result print(@RequestBody Map<String,Object> list) {
return null;
public void print(@RequestBody Map<String,Object> list, HttpServletResponse response) {
if(list==null||list.isEmpty()) return;
ComStatsTemplate template =LisUtil.mapToEntity(list,"Template",ComStatsTemplate.class);
if(template==null ) return;
Result res = statsService.query(list);
if (res == null) return;
if (!"0".equals(res.getCode())) return;
// 数据集合:安全转换
// 1. 数据集合:类型有保证,直接强转
@SuppressWarnings("unchecked")
List<Map<String, Object>> dataList = (List<Map<String, Object>>) res.getData();
if (dataList == null) {
dataList = new ArrayList<>();
}
// 列配置 + 字典缓存(入参引入)
List<ComStatsParameter> columnList = LisUtil.mapToEntityList(list, "Column", ComStatsParameter.class);
Map<String, EasyExcelExportUtil.ColumnConfig> columnMap = new LinkedHashMap<>();
Map<String, Map<String, String>> dictCache = new HashMap<>();
if (columnList != null) {
columnList.sort(Comparator.comparing(ComStatsParameter::getParamSequence,
Comparator.nullsLast(Comparator.naturalOrder())));
for (ComStatsParameter column : columnList) {
Long sequence = column.getParamSequence();
if (sequence == null || sequence == 0) continue;
String code = column.getParamCode();
if (code == null || code.trim().isEmpty()) continue;
String name = column.getParamName();
if (name == null) name = "";
Integer width = column.getParamWidth() == null ? null : column.getParamWidth().intValue();
boolean isSum ="6".equals(column.getParamType());
columnMap.put(code, new EasyExcelExportUtil.ColumnConfig(name, width,isSum));
String zdlb = column.getRemark();
if (zdlb != null && !zdlb.trim().isEmpty()) {
dictCache.put(code, statsService.getDictCache(zdlb));
}
}
}
String title=template.getStatsTitle();
// 生成 Excel 写入响应流
try {
PdfExportUtil.printPdf(response,
"导出_" + System.currentTimeMillis(), title, dataList, columnMap, dictCache);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

View File

@ -90,7 +90,6 @@
<if test="whonet_mic != null and whonet_mic != ''">whonet_mic=#{whonet_mic},</if>
<if test="whonet_etest != null and whonet_etest != ''">whonet_etest=#{whonet_etest},</if>
<if test="whonet != null and whonet != ''">whonet=#{whonet},</if>
ywdh
</set>
where ywdh = #{ywdh}
</update>

View File

@ -10,28 +10,33 @@
</where>
</select>
<insert id="add">
<insert id="add">
insert into xm_medgroup(
<if test="ywdh != null and ywdh != ''">ywdh,</if>
<if test="bz != null and bz != ''">bz,</if>
<if test="nymic != null ">nymic,</if>
<if test="zjmic != null ">zjmic,</if>
<if test="ismic != null and ismic != ''">ismic,</if>
ywzmc, xh
)values(
<if test="ywdh != null and ywdh != ''">#{ywdh},</if>
<if test="bz != null and bz != ''">#{bz},</if>
<if test="nymic != null ">#{nymic},</if>
<if test="zjmic != null ">#{zjmic},</if>
<if test="ismic != null and ismic != ''">#{ismic},</if>
#{ywzmc}, #{xh}
)
</insert>
<update id="update">
update xm_medgroup set
<if test="ywdh != null and ywdh != ''"> ywdh = #{ywdh},</if>
<if test="bz != null and bz != ''"> bz = #{bz},</if>
<if test="nymic != null"> nymic = #{nymic},</if>
ywzmc = #{ywzmc}
<update id="update">
update xm_medgroup
<set>
<if test="ywdh != null and ywdh != ''"> ywdh = #{ywdh},</if>
<if test="bz != null and bz != ''"> bz = #{bz},</if>
<if test="nymic != null"> nymic = #{nymic},</if>
<if test="zjmic != null"> zjmic = #{zjmic},</if>
<if test="ismic != null and ismic != ''"> ismic = #{ismic},</if>
</set>
where ywzmc = #{ywzmc} and xh = #{xh}
</update>

View File

@ -22,10 +22,8 @@
update xm_medinter
<set>
<if test="ywdh != null and ywdh != ''">ywdh = #{ywdh},</if>
<if test="tdh != null and tdh != ''">tdh = #{tdh},</if>
yq = #{yq}
</set>
where yq = #{yq}
where yq = #{yq} and tdh = #{tdh}
</update>
<delete id="delete">