Przeglądaj źródła

perf(order): 优化导出功能性能与单元格合并逻辑

- OrderItemMapper新增批量查询订单项方法getListByOrderNumbers,减少数据库查询次数
- 导出订单列表时,改为批量获取订单项,避免N+1查询问题,提升导出效率
- ExportUtils中创建单元格方法优化,添加快速路径处理无合并单元格情况,提升处理速度
- OrderServiceImpl中导出总计行使用工作簿样式设置,调整背景色及边框样式
- 总计行添加额外列填充空白单元格,保持统一格式美观
- 统一导出相关单元格样式,数据格式化并优化显示效果
SheepHy 2 dni temu
rodzic
commit
19bbcb4ed7

+ 7 - 0
yami-shop-service/src/main/java/com/yami/shop/dao/OrderItemMapper.java

@@ -40,6 +40,13 @@ public interface OrderItemMapper extends BaseMapper<OrderItem> {
      */
     OrderItem findBySkuIdAndOrderNumber(@Param("skuId")Long skuId,@Param("orderNumber") String orderNumber);
 
+    /**
+     * 批量查询订单项(用于导出优化)
+     * @param orderNumbers 订单编号列表
+     * @return 订单项列表
+     */
+    List<OrderItem> getListByOrderNumbers(@Param("orderNumbers") List<String> orderNumbers);
+
 //    List<OrderItem> getUnCloseRefundOrderItemByOrderNumber(String orderNumber);
 
 //	void insertBatch(List<OrderItem> orderItems);

+ 53 - 19
yami-shop-service/src/main/java/com/yami/shop/service/impl/OrderServiceImpl.java

@@ -62,10 +62,7 @@ import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.poi.ss.usermodel.CellStyle;
-import org.apache.poi.ss.usermodel.Row;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.*;
 import org.apache.poi.xssf.streaming.SXSSFSheet;
 import org.apache.poi.xssf.streaming.SXSSFWorkbook;
 import org.springframework.beans.BeanUtils;
@@ -1932,16 +1929,26 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
         @Override
         public R<String> export (BackendOrderParam orderParam, Long userId) {
             List<Order> orderList = orderMapper.findList(orderParam);
-            if (!orderList.isEmpty()) {
-                orderList.forEach(c -> {
-                    List<OrderItem> orderItems = orderItemMapper.getListByOrderNumber(c.getOrderNumber());
-                    c.setOrderItems(orderItems);
-                    c.setGoodsTotalCount(orderItems.stream().map(OrderItem::getProdCount).reduce(0, Integer::sum));
-                });
-            }
             if (orderList.isEmpty()) {
                 return R.FAIL("未查询到数据不允许导出");
             }
+            
+            // 性能优化:批量查询所有订单项,避免N+1查询问题
+            List<String> orderNumbers = orderList.stream()
+                    .map(Order::getOrderNumber)
+                    .collect(Collectors.toList());
+            List<OrderItem> allOrderItems = orderItemMapper.getListByOrderNumbers(orderNumbers);
+            
+            // 按订单编号分组
+            Map<String, List<OrderItem>> orderItemMap = allOrderItems.stream()
+                    .collect(Collectors.groupingBy(OrderItem::getOrderNumber));
+            
+            // 设置订单项
+            orderList.forEach(c -> {
+                List<OrderItem> orderItems = orderItemMap.getOrDefault(c.getOrderNumber(), Collections.emptyList());
+                c.setOrderItems(orderItems);
+                c.setGoodsTotalCount(orderItems.stream().map(OrderItem::getProdCount).reduce(0, Integer::sum));
+            });
             String taskId = UUID.randomUUID().toString();
             ExportTask exportTask = new ExportTask();
             exportTask.setUserId(userId);
@@ -2042,7 +2049,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
                 ExportUtils.createColumnHeaders(sheet, headerStyle, headers);
 
                 // 计算并创建总计行
-                createSummaryRow(sheet, orderList, dataStyle, numberStyle);
+                createSummaryRow(sheet, orderList, workbook);
 
                 // 填充数据
                 fillOrderData(sheet, orderList, dataStyle, numberStyle, dateStyle, context);
@@ -2109,7 +2116,24 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
         /**
          * 创建总计行
          */
-        private void createSummaryRow(Sheet sheet, List<Order> orderList, CellStyle dataStyle, CellStyle numberStyle) {
+        private void createSummaryRow(Sheet sheet, List<Order> orderList, Workbook workbook) {
+            // 创建总计行样式(浅绿色背景)
+            CellStyle summaryStyle = workbook.createCellStyle();
+            summaryStyle.setFillForegroundColor(IndexedColors.LIGHT_GREEN.getIndex());
+            summaryStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+            summaryStyle.setBorderTop(BorderStyle.THIN);
+            summaryStyle.setBorderBottom(BorderStyle.THIN);
+            summaryStyle.setBorderLeft(BorderStyle.THIN);
+            summaryStyle.setBorderRight(BorderStyle.THIN);
+            summaryStyle.setAlignment(HorizontalAlignment.CENTER);
+            summaryStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+
+            // 创建总计行数字样式(浅绿色背景)
+            CellStyle summaryNumberStyle = workbook.createCellStyle();
+            summaryNumberStyle.cloneStyleFrom(summaryStyle);
+            DataFormat dataFormat = workbook.createDataFormat();
+            summaryNumberStyle.setDataFormat(dataFormat.getFormat("#,##0.00"));
+
             // 计算总计值
             int totalProductCount = 0;      // 商品总数量
             double totalAmount = 0;         // 商品总额
@@ -2149,22 +2173,32 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
             summaryRow.setHeightInPoints(20);
 
             // 序号列显示"总计"
-            ExportUtils.createCell(sheet, summaryRow, 0, 0, 2, "总计", dataStyle);
+            ExportUtils.createCell(sheet, summaryRow, 0, 0, 2, "总计", summaryStyle);
+
+            // 其他列填充空白单元格(保持背景色)
+            for (int i = 1; i <= 12; i++) {
+                ExportUtils.createCell(sheet, summaryRow, i, 0, 2, "", summaryStyle);
+            }
 
             // 商品总数量(索引13)
-            ExportUtils.createCell(sheet, summaryRow, 13, 0, 2, totalProductCount, numberStyle);
+            ExportUtils.createCell(sheet, summaryRow, 13, 0, 2, totalProductCount, summaryNumberStyle);
 
             // 商品总额(索引14)
-            ExportUtils.createCell(sheet, summaryRow, 14, 0, 2, totalAmount, numberStyle);
+            ExportUtils.createCell(sheet, summaryRow, 14, 0, 2, totalAmount, summaryNumberStyle);
 
             // 运费(索引15)
-            ExportUtils.createCell(sheet, summaryRow, 15, 0, 2, totalFreight, numberStyle);
+            ExportUtils.createCell(sheet, summaryRow, 15, 0, 2, totalFreight, summaryNumberStyle);
 
             // 积分抵扣(索引16,显示为负数,四舍五入保留后4位)
-            ExportUtils.createCell(sheet, summaryRow, 16, 0, 2, "-" + Arith.round(totalOffsetPoints, 4), numberStyle);
+            ExportUtils.createCell(sheet, summaryRow, 16, 0, 2, "-" + Arith.round(totalOffsetPoints, 4), summaryNumberStyle);
 
             // 实付金额(索引17)
-            ExportUtils.createCell(sheet, summaryRow, 17, 0, 2, totalActualAmount, numberStyle);
+            ExportUtils.createCell(sheet, summaryRow, 17, 0, 2, totalActualAmount, summaryNumberStyle);
+
+            // 后续列填充空白单元格(保持背景色)
+            for (int i = 18; i <= 20; i++) {
+                ExportUtils.createCell(sheet, summaryRow, i, 0, 2, "", summaryStyle);
+            }
         }
 
         /**

+ 38 - 2
yami-shop-service/src/main/java/com/yami/shop/utils/ExportUtils.java

@@ -112,6 +112,13 @@ public class ExportUtils {
      * 创建单元格(字符串类型)
      */
     public static void createCell(Sheet sheet,Row row,int col, int size,int rowNum, String value, CellStyle style) {
+        // 快速路径:不需要合并时直接创建单元格
+        if (size <= 1) {
+            Cell cell = row.createCell(col);
+            cell.setCellValue(value != null ? value : "");
+            cell.setCellStyle(style);
+            return;
+        }
         int lastRow = rowNum + size - 1;
         if (lastRow > rowNum) {
             if (!isMergeRegionOverlap(sheet, rowNum-1, lastRow-1, col)){ //因为索引是从0开始的  所以开始行和结束行都需要减1
@@ -127,9 +134,20 @@ public class ExportUtils {
     }
 
     /**
-     * 创建单元格(数字类型)
+     * 创建单元格(数字类型 - BigDecimal
      */
     public static void createCell(Sheet sheet, Row row, int col, int size, int rowNum, BigDecimal value, CellStyle style) {
+        // 快速路径:不需要合并时直接创建单元格
+        if (size <= 1) {
+            Cell cell = row.createCell(col);
+            if (value != null) {
+                cell.setCellValue(value.doubleValue());
+            } else {
+                cell.setCellValue(0);
+            }
+            cell.setCellStyle(style);
+            return;
+        }
         int lastRow = rowNum + size - 1;
         if (lastRow > rowNum) {
             if (!isMergeRegionOverlap(sheet, rowNum-1, lastRow-1, col)){
@@ -147,9 +165,16 @@ public class ExportUtils {
         cell.setCellStyle(style);
     }
     /**
-     * 创建单元格(数字类型)
+     * 创建单元格(数字类型 - double
      */
     public static void createCell(Sheet sheet,Row row,int col, int size,int rowNum, double value, CellStyle style) {
+        // 快速路径:不需要合并时直接创建单元格
+        if (size <= 1) {
+            Cell cell = row.createCell(col);
+            cell.setCellValue(value);
+            cell.setCellStyle(style);
+            return;
+        }
         int lastRow = rowNum + size - 1;
         if (lastRow > rowNum) {
             if (!isMergeRegionOverlap(sheet, rowNum-1, lastRow-1, col)){
@@ -167,6 +192,17 @@ public class ExportUtils {
      * 创建单元格(整数类型)
      */
     public static void createCell(Sheet sheet,Row row,int col, int size,int rowNum,  Integer value, CellStyle style) {
+        // 快速路径:不需要合并时直接创建单元格
+        if (size <= 1) {
+            Cell cell = row.createCell(col);
+            if (value != null) {
+                cell.setCellValue(value);
+            } else {
+                cell.setCellValue(0);
+            }
+            cell.setCellStyle(style);
+            return;
+        }
         int lastRow = rowNum + size - 1;
         if (lastRow > rowNum) {
             if (!isMergeRegionOverlap(sheet, rowNum-1, lastRow-1, col)){

+ 11 - 0
yami-shop-service/src/main/resources/mapper/OrderItemMapper.xml

@@ -94,4 +94,15 @@
         select * from tz_order_item where order_number = #{orderNumber} and sku_id =#{skuId}
     </select>
 
+    <!-- 批量查询订单项(用于导出优化) -->
+    <select id="getListByOrderNumbers" resultType="com.yami.shop.bean.model.OrderItem">
+        SELECT sku.spec, sku.hb_sku_id, sku.sku_code, oi.*
+        FROM tz_order_item oi
+        LEFT JOIN tz_sku sku ON oi.sku_id = sku.sku_id
+        WHERE oi.order_number IN
+        <foreach collection="orderNumbers" item="orderNumber" open="(" close=")" separator=",">
+            #{orderNumber}
+        </foreach>
+    </select>
+
 </mapper>