

bash
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
java
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
//工具类导出(支持1单1商品,1单多商品)
public class ExcelExportUtil {
public static String convertSpec(String spec) {
JSONArray array = JSONUtil.parseArray(spec);
return array.stream().map(obj -> {
cn.hutool.json.JSONObject o = (cn.hutool.json.JSONObject) obj;
return o.getStr("name") + ":" + o.getStr("value");
}).collect(Collectors.joining(" "));
}
public static void putIfSelected(Map<String, Object> map, String field, Object value, List<String> exportFields) {
if (exportFields.contains(field)) {
map.put(field, value);
}
}
//表头样式
private static CellStyle createHeadStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
//背景色(浅灰)
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
//字体
Font font = workbook.createFont();
font.setFontName("微软雅黑");
font.setFontHeightInPoints((short) 15);
font.setBold(true);
style.setFont(font);
//对齐
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
//边框
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
//数据样式
private static CellStyle createDataStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontName("微软雅黑");
font.setFontHeightInPoints((short) 11);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
return style;
}
//初始化
private static void initResponse(HttpServletResponse response, String sheetName) throws UnsupportedEncodingException {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(sheetName, "UTF-8") + ".xlsx");
}
//---------------------------售后导出-------------------------------
//自定义字段普通导出
public static void export(HttpServletResponse response, List<Map<String, Object>> data, List<String> fields, String sheetName) throws IOException {
initResponse(response, sheetName);
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet(sheetName);
writeSheet(workbook, sheet, data, fields, false);
workbook.write(response.getOutputStream());
workbook.close();
}
//---------------------------一键下单导出-------------------------------
//自定义字段订单信息合并导出
public static void exportOrder(HttpServletResponse response, List<Map<String, Object>> data, List<String> fields, String sheetName) throws IOException {
initResponse(response, sheetName);
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet(sheetName);
writeSheet(workbook, sheet, data, fields, true);
workbook.write(response.getOutputStream());
workbook.close();
}
//写sheet
private static void writeSheet(Workbook workbook, Sheet sheet, List<Map<String, Object>> data, List<String> fields, boolean mergeOrder) {
CellStyle headStyle = createHeadStyle(workbook);
CellStyle dataStyle = createDataStyle(workbook);
writeHeader(sheet, fields, headStyle);
writeRows(sheet, data, fields, dataStyle, mergeOrder);
}
//创建表头
private static void writeHeader(Sheet sheet, List<String> fields, CellStyle headStyle) {
Row headRow = sheet.createRow(0);
headRow.setHeightInPoints(30);
for (int i = 0; i < fields.size(); i++) {
Cell cell = headRow.createCell(i);
cell.setCellValue(fields.get(i));
cell.setCellStyle(headStyle);
sheet.setColumnWidth(i, 20 * 256);
}
}
//写数据
private static void writeRows(Sheet sheet, List<Map<String, Object>> data, List<String> fields, CellStyle dataStyle, boolean mergeOrder) {
int rowIndex = 1;
int mergeStartRow = 1;
String lastOrderId = "";
for (Map<String, Object> rowData : data) {
String currentOrderId = String.valueOf(rowData.getOrDefault("订单编号", ""));
//合并逻辑只在 mergeOrder = true 时生效
if (mergeOrder && !lastOrderId.equals(currentOrderId)) {
if (!lastOrderId.isEmpty()) {
mergeOrderCells(sheet, mergeStartRow, rowIndex - 1, fields);
}
mergeStartRow = rowIndex;
}
Row row = sheet.createRow(rowIndex++);
for (int i = 0; i < fields.size(); i++) {
String field = fields.get(i);
Object value = rowData.get(field);
Cell cell = row.createCell(i);
cell.setCellStyle(dataStyle);
if (value == null) {
cell.setCellValue("");
} else if (value instanceof Number) {
cell.setCellValue(((Number) value).doubleValue());
} else {
cell.setCellValue(value.toString());
}
}
lastOrderId = currentOrderId;
}
// 最后一个订单合并
if (mergeOrder && !lastOrderId.isEmpty()) {
mergeOrderCells(sheet, mergeStartRow, rowIndex - 1, fields);
}
}
// ===== 辅助方法:合并订单字段的单元格 =====
private static void mergeOrderCells(Sheet sheet, int startRow, int endRow, List<String> fields) {
// 如果起始行大于或等于结束行,说明只有一行数据,不需要合并,直接返回
if (startRow >= endRow) {
return;
}
for (int i = 0; i < fields.size(); i++) {
String fieldName = fields.get(i);
// 如果是订单字段,则合并
if (ORDER_FIELDS.contains(fieldName)) {
sheet.addMergedRegion(new CellRangeAddress(startRow, endRow, i, i));
}
}
}
//定义合并的表头
private static final Set<String> ORDER_FIELDS = Set.of(
"订单编号", "店铺名称", "店铺ID", "成交时间", "订单状态", "实收款",
"支付方式", "发货时间", "旗帜颜色", "用户留言", "店铺备注", "软件备注"
);
}
java
@PostMapping("/export")
public void export(HttpServletResponse response, @Valid @RequestBody OrderReq req) throws IOException {
List<OrderVO> list = baseService.getOneClickOrderList(req); //换成自己的查询列表
Assert.isFalse(StringUtils.isEmpty(list), "导出失败,数据为空");
List<Map<String, Object>> data = otherService.buildOrderExportData(list, req.getExportFields());
ExcelExportUtil.exportOrder(response, data, req.getExportFields(), "oneClickOrder");
}
//业务层,整合数据
public List<Map<String, Object>> buildOrderExportData(List<OrderVO> list, List<String> exportFields) {
List<Map<String, Object>> result = new ArrayList<>();
for (OrderVO vo : list) {
for (ProductVO p : vo.getProductVOS()) {
//-----------------------订单信息---------------------
Map<String, Object> map = new HashMap<>();
ExcelExportUtil.putIfSelected(map, "订单编号", vo.getOrderId(), exportFields);
ExcelExportUtil.putIfSelected(map, "店铺名称", vo.getShopName(), exportFields);
ExcelExportUtil.putIfSelected(map, "店铺ID", vo.getShopId(), exportFields);
ExcelExportUtil.putIfSelected(map, "成交时间", vo.getPayTime(), exportFields);
ExcelExportUtil.putIfSelected(map, "订单状态", vo.getOrderStatusDesc(), exportFields);
ExcelExportUtil.putIfSelected(map, "实收款", vo.getActualReceiveAmount(), exportFields);
ExcelExportUtil.putIfSelected(map, "支付方式", TagConstants.getDesc(vo.getPayType()), exportFields);
ExcelExportUtil.putIfSelected(map, "发货时间", vo.getShipTimeStr(), exportFields);
ExcelExportUtil.putIfSelected(map, "旗帜颜色", TagConstants.getStarsDesc(vo.getSellerRemarkStars()), exportFields);
ExcelExportUtil.putIfSelected(map, "用户留言", vo.getBuyerWords(), exportFields);
ExcelExportUtil.putIfSelected(map, "店铺备注", vo.getSellerWords(), exportFields);
ExcelExportUtil.putIfSelected(map, "软件备注", vo.getLocalRemark(), exportFields);
//-----------------------商品信息---------------------
String afterSaleStatus = p.getAfterSaleStatus() == 0 ?
p.getProductOrderStatusDesc() : AfterSaleConstants.getAfterSaleStatusDesc(p.getAfterSaleStatus());
ExcelExportUtil.putIfSelected(map, "商品标题", p.getProductName(), exportFields);
ExcelExportUtil.putIfSelected(map, "商品ID", p.getProductId(), exportFields);
ExcelExportUtil.putIfSelected(map, "商品单价", p.getProductOrderAmount(), exportFields);
ExcelExportUtil.putIfSelected(map, "商品数量", p.getItemNum(), exportFields);
ExcelExportUtil.putIfSelected(map, "售后状态", afterSaleStatus, exportFields);
ExcelExportUtil.putIfSelected(map, "商品实收", p.getProductPayAmount(), exportFields);
ExcelExportUtil.putIfSelected(map, "运费", p.getPostAmount(), exportFields);
ExcelExportUtil.putIfSelected(map, "运费优惠", p.getPostInsuranceAmount(), exportFields);
ExcelExportUtil.putIfSelected(map, "规格", ExcelExportUtil.convertSpec(p.getSpec()), exportFields);
ExcelExportUtil.putIfSelected(map, "商品编码", p.getCode(), exportFields);
if (StringUtils.isNotEmpty(p.getAlProductVOList())) {
AlProductVO item = p.getAlProductVOList().get(0);
ExcelExportUtil.putIfSelected(map, "物流名称", item.getLogisticsCompany(), exportFields);
ExcelExportUtil.putIfSelected(map, "物流单号", item.getBillNo(), exportFields);
}
result.add(map);
}
}
return result;
}