Quellcode durchsuchen

feat(order): 优化导出订单功能,添加海博商品信息及总计行

- OrderItem模型新增海博商品ID和商品编码字段
- 查询订单列表时联表获取海博商品ID和商品编码
- 导出Excel表头添加海博商品ID和商品编码两列
- 修改导出逻辑,订单每个商品单独一行显示,取消合并单元格
- 计算并新增订单商品总计行,统计商品总数量、总额、运费、积分抵扣及实付金额
- 按商品数量比例分摊运费、积分抵扣和实付金额到每个商品行
- 调整列宽,保证表格显示合理美观
SheepHy vor 2 Tagen
Ursprung
Commit
3bbf904261

+ 12 - 0
yami-shop-bean/src/main/java/com/yami/shop/bean/model/OrderItem.java

@@ -205,4 +205,16 @@ public class OrderItem implements Serializable {
     @TableField(exist = false)
     private String spec;
 
+    /**
+     * 海博商品ID
+     */
+    @TableField(exist = false)
+    private String hbSkuId;
+
+    /**
+     * 商品编码
+     */
+    @TableField(exist = false)
+    private String skuCode;
+
 }

+ 133 - 70
yami-shop-service/src/main/java/com/yami/shop/service/impl/OrderServiceImpl.java

@@ -2031,16 +2031,19 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
 
                 String[] headers = {
                         "序号", "订单编号", "下单时间", "付款时间", "订单状态", "订单类型",
-                        "商品名称", "规格", "单价(元)", "数量", "小计(元)",
+                        "商品名称", "海博商品ID", "商品编码", "规格", "单价(元)", "数量", "小计(元)",
                         "商品总数量", "商品总额(元)", "运费(元)", "积分抵扣(元)",
                         "实付金额", "所属企业", "买家姓名", "买家电话"
                 };
                 // 创建合并单元格的表头
-                ExportUtils.createMergedHeader(sheet, headerStyle, "正常订单",18);
+                ExportUtils.createMergedHeader(sheet, headerStyle, "正常订单",20);
 
                 // 创建列标题行
                 ExportUtils.createColumnHeaders(sheet, headerStyle, headers);
 
+                // 计算并创建总计行
+                createSummaryRow(sheet, orderList, dataStyle, numberStyle);
+
                 // 填充数据
                 fillOrderData(sheet, orderList, dataStyle, numberStyle, dateStyle, context);
                 // 写入文件
@@ -2087,18 +2090,81 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
             sheet.setColumnWidth(4, 10 * 256);   // 订单状态
             sheet.setColumnWidth(5, 8 * 256);    // 订单类型
             sheet.setColumnWidth(6, 25 * 256);   // 商品名称
-            sheet.setColumnWidth(7, 20 * 256);   // 规格
-            sheet.setColumnWidth(8, 10 * 256);   // 单价
-            sheet.setColumnWidth(9, 8 * 256);    // 数量
-            sheet.setColumnWidth(10, 10 * 256);   // 小计
-            sheet.setColumnWidth(11, 12 * 256);  // 商品总数量
-            sheet.setColumnWidth(12, 12 * 256);  // 商品总额
-            sheet.setColumnWidth(13, 8 * 256);   // 运费
-            sheet.setColumnWidth(14, 12 * 256);  // 积分抵扣
-            sheet.setColumnWidth(15, 10 * 256);  // 实付金额
-            sheet.setColumnWidth(16, 15 * 256);  // 所属企业
-            sheet.setColumnWidth(17, 10 * 256);  // 买家姓名
-            sheet.setColumnWidth(18, 15 * 256);  // 买家电话
+            sheet.setColumnWidth(7, 15 * 256);   // 海博商品ID
+            sheet.setColumnWidth(8, 15 * 256);   // 商品编码
+            sheet.setColumnWidth(9, 20 * 256);   // 规格
+            sheet.setColumnWidth(10, 10 * 256);  // 单价
+            sheet.setColumnWidth(11, 8 * 256);   // 数量
+            sheet.setColumnWidth(12, 10 * 256);  // 小计
+            sheet.setColumnWidth(13, 12 * 256);  // 商品总数量
+            sheet.setColumnWidth(14, 12 * 256);  // 商品总额
+            sheet.setColumnWidth(15, 8 * 256);   // 运费
+            sheet.setColumnWidth(16, 12 * 256);  // 积分抵扣
+            sheet.setColumnWidth(17, 10 * 256);  // 实付金额
+            sheet.setColumnWidth(18, 15 * 256);  // 所属企业
+            sheet.setColumnWidth(19, 10 * 256);  // 买家姓名
+            sheet.setColumnWidth(20, 15 * 256);  // 买家电话
+        }
+
+        /**
+         * 创建总计行
+         */
+        private void createSummaryRow(Sheet sheet, List<Order> orderList, CellStyle dataStyle, CellStyle numberStyle) {
+            // 计算总计值
+            int totalProductCount = 0;      // 商品总数量
+            double totalAmount = 0;         // 商品总额
+            double totalFreight = 0;        // 运费
+            double totalOffsetPoints = 0;   // 积分抵扣
+            double totalActualAmount = 0;   // 实付金额
+
+            for (Order order : orderList) {
+                for (OrderItem item : order.getOrderItems()) {
+                    // 商品总数量(每行的商品数量)
+                    totalProductCount += item.getProdCount() != null ? item.getProdCount() : 0;
+                    
+                    // 商品总额(每行的商品总额是订单总额,需要按商品数量比例分摊)
+                    if (order.getTotal() != null && order.getProductNums() > 0) {
+                        totalAmount += Arith.div(order.getTotal(), order.getProductNums()) * item.getProdCount();
+                    }
+                    
+                    // 运费(按商品数量比例分摊)
+                    if (order.getFreightAmount() != null && order.getProductNums() > 0) {
+                        totalFreight += Arith.div(order.getFreightAmount(), order.getProductNums()) * item.getProdCount();
+                    }
+                    
+                    // 积分抵扣(按商品数量比例分摊)
+                    if (order.getOffsetPoints() != null && order.getProductNums() > 0) {
+                        totalOffsetPoints += Arith.div(Arith.div(Double.valueOf(order.getOffsetPoints()), 100), order.getProductNums()) * item.getProdCount();
+                    }
+                    
+                    // 实付金额(按商品数量比例分摊)
+                    if (order.getActualTotal() != null && order.getProductNums() > 0) {
+                        totalActualAmount += Arith.div(order.getActualTotal(), order.getProductNums()) * item.getProdCount();
+                    }
+                }
+            }
+
+            // 创建总计行(第3行,索引2)
+            Row summaryRow = sheet.createRow(2);
+            summaryRow.setHeightInPoints(20);
+
+            // 序号列显示"总计"
+            ExportUtils.createCell(sheet, summaryRow, 0, 0, 2, "总计", dataStyle);
+
+            // 商品总数量(索引13)
+            ExportUtils.createCell(sheet, summaryRow, 13, 0, 2, totalProductCount, numberStyle);
+
+            // 商品总额(索引14)
+            ExportUtils.createCell(sheet, summaryRow, 14, 0, 2, totalAmount, numberStyle);
+
+            // 运费(索引15)
+            ExportUtils.createCell(sheet, summaryRow, 15, 0, 2, totalFreight, numberStyle);
+
+            // 积分抵扣(索引16,显示为负数)
+            ExportUtils.createCell(sheet, summaryRow, 16, 0, 2, "-" + totalOffsetPoints, numberStyle);
+
+            // 实付金额(索引17)
+            ExportUtils.createCell(sheet, summaryRow, 17, 0, 2, totalActualAmount, numberStyle);
         }
 
         /**
@@ -2108,7 +2174,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
                 CellStyle dataStyle, CellStyle numberStyle, CellStyle dateStyle,ExportContext context)  throws InterruptedException{
             int batchSize = 100; // 每批处理1000条
             int totalRows = orderList.size();
-            int rowNum = 2; // 从第3行开始(0-based索引)
+            int rowNum = 3; // 从第4行开始(0-based索引),第3行是总计行
             int indexNum = 1; //序号
             for (int i = 0; i < totalRows; i += batchSize) {
                 int end = Math.min(i + batchSize, totalRows);
@@ -2120,13 +2186,16 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
                         break;
 //                    throw new InterruptedException("导出被中断");
                     }
-                    Row row = sheet.createRow(rowNum++);
-                    row.setHeightInPoints(18);
-                    //序号
-                    ExportUtils.createCell(sheet, row, 0, order.getOrderItems().size(), rowNum, indexNum, dataStyle);
-                    fillOrderRowData(sheet, rowNum, row, order, dataStyle, numberStyle, dateStyle);
-                    rowNum += order.getOrderItems().size() - 1;
-                    indexNum++;
+                    // 为每个订单项创建单独的行
+                    for (int itemIndex = 0; itemIndex < order.getOrderItems().size(); itemIndex++) {
+                        Row row = sheet.createRow(rowNum++);
+                        row.setHeightInPoints(18);
+                        OrderItem item = order.getOrderItems().get(itemIndex);
+                        // 序号(每行独立)
+                        ExportUtils.createCell(sheet, row, 0, 0, rowNum, indexNum++, dataStyle);
+                        // 填充订单数据和商品数据
+                        fillOrderRowDataWithItem(sheet, rowNum, row, order, item, dataStyle, numberStyle, dateStyle);
+                    }
                 }
 
                 // 定期刷新到磁盘
@@ -2143,80 +2212,74 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
         }
 
         /**
-         * 填充单行订单数据
+         * 填充单行订单数据(包含订单信息和单个商品信息,每行独立不合并)
          */
-        private void fillOrderRowData (Sheet sheet,int rowNum, Row row, Order order,
-                CellStyle dataStyle, CellStyle numberStyle, CellStyle dateStyle){
+        private void fillOrderRowDataWithItem(Sheet sheet, int rowNum, Row row, Order order,
+                OrderItem item, CellStyle dataStyle, CellStyle numberStyle, CellStyle dateStyle) {
 
             // 订单编号
-            ExportUtils.createCell(sheet, row, 1, order.getOrderItems().size(), rowNum, order.getOrderNumber(), dataStyle);
+            ExportUtils.createCell(sheet, row, 1, 0, rowNum, order.getOrderNumber(), dataStyle);
 
             // 下单时间
-            ExportUtils.createCell(sheet, row, 2, order.getOrderItems().size(), rowNum, ExportUtils.formatDate(order.getCreateTime()), dateStyle);
+            ExportUtils.createCell(sheet, row, 2, 0, rowNum, ExportUtils.formatDate(order.getCreateTime()), dateStyle);
 
             // 付款时间
-            ExportUtils.createCell(sheet, row, 3, order.getOrderItems().size(), rowNum, ExportUtils.formatDate(order.getPayTime()), dateStyle);
+            ExportUtils.createCell(sheet, row, 3, 0, rowNum, ExportUtils.formatDate(order.getPayTime()), dateStyle);
 
             // 订单状态
-            ExportUtils.createCell(sheet, row, 4, order.getOrderItems().size(), rowNum, getHbOrderStatus(order.getHbOrderStatus()), dataStyle);
+            ExportUtils.createCell(sheet, row, 4, 0, rowNum, getHbOrderStatus(order.getHbOrderStatus()), dataStyle);
 
             // 订单类型
-            ExportUtils.createCell(sheet, row, 5, order.getOrderItems().size(), rowNum, getDvyType(order.getDvyType()), dataStyle);
-            // 商品明细行(每个订单项一行)
-            fillOrderItems(sheet, rowNum, row, order, dataStyle, numberStyle);
+            ExportUtils.createCell(sheet, row, 5, 0, rowNum, getDvyType(order.getDvyType()), dataStyle);
 
-            // 商品总数量
-            ExportUtils.createCell(sheet, row, 11, order.getOrderItems().size(), rowNum, order.getProductNums(), dataStyle);
+            // 商品名称
+            ExportUtils.createCell(sheet, row, 6, 0, rowNum, item.getProdName(), dataStyle);
 
-            // 商品总额(元)
-            ExportUtils.createCell(sheet, row, 12, order.getOrderItems().size(), rowNum, order.getTotal(), numberStyle);
+            // 海博商品ID
+            ExportUtils.createCell(sheet, row, 7, 0, rowNum, item.getHbSkuId(), dataStyle);
 
-            // 运费(元)
-            ExportUtils.createCell(sheet, row, 13, order.getOrderItems().size(), rowNum, order.getFreightAmount(), numberStyle);
+            // 商品编码
+            ExportUtils.createCell(sheet, row, 8, 0, rowNum, item.getSkuCode(), dataStyle);
 
-            // 积分抵扣(元) - 显示为负数
-            ExportUtils.createCell(sheet, row, 14, order.getOrderItems().size(), rowNum, "-" + (order.getOffsetPoints() == null ? 0 : Arith.div(Double.valueOf(order.getOffsetPoints()), 100)), numberStyle);
+            // 规格
+            ExportUtils.createCell(sheet, row, 9, 0, rowNum, item.getSpec(), dataStyle);
 
-            // 实付金额
-            ExportUtils.createCell(sheet, row, 15, order.getOrderItems().size(), rowNum, order.getActualTotal(), numberStyle);
+            // 单价(元)
+            ExportUtils.createCell(sheet, row, 10, 0, rowNum, item.getPrice(), numberStyle);
 
-            // 所属企业
-            ExportUtils.createCell(sheet, row, 16, order.getOrderItems().size(), rowNum, order.getChannelName(), dataStyle);
+            // 数量
+            ExportUtils.createCell(sheet, row, 11, 0, rowNum, item.getProdCount(), dataStyle);
 
-            // 买家姓名
-            ExportUtils.createCell(sheet, row, 17, order.getOrderItems().size(), rowNum, order.getReceiver(), dataStyle);
+            // 小计(元)
+            ExportUtils.createCell(sheet, row, 12, 0, rowNum, item.getProductTotalAmount(), numberStyle);
 
-            // 买家电话
-            ExportUtils.createCell(sheet, row, 18, order.getOrderItems().size(), rowNum, order.getUserMobile(), dataStyle);
-        }
+            // 商品总数量(使用当前商品数量)
+            ExportUtils.createCell(sheet, row, 13, 0, rowNum, item.getProdCount(), dataStyle);
 
-        /**
-         * 填充订单项明细
-         */
-        private void fillOrderItems (Sheet sheet,int rowNum, Row row, Order order,
-                CellStyle dataStyle, CellStyle numberStyle){
-            int itemSize = order.getOrderItems().size();
-
-            for (int i = 0; i < itemSize; i++) {
-                Row currentRow = (i < 1) ? row : sheet.createRow(rowNum + (i - 1));
-                OrderItem item = order.getOrderItems().get(i);
+            // 商品总额(元)
+            ExportUtils.createCell(sheet, row, 14, 0, rowNum, order.getTotal(), numberStyle);
 
-                // 商品名称
-                ExportUtils.createCell(sheet, currentRow, 6, 0, rowNum, item.getProdName(), dataStyle);
+            // 运费(元) - 按商品数量比例分摊
+            double freightPerItem = order.getProductNums() > 0 ? Arith.div(order.getFreightAmount(), order.getProductNums()) * item.getProdCount() : 0;
+            ExportUtils.createCell(sheet, row, 15, 0, rowNum, freightPerItem, numberStyle);
 
-                // 规格
-                ExportUtils.createCell(sheet, currentRow, 7, 0, rowNum, item.getSpec(), dataStyle);
+            // 积分抵扣(元) - 按商品数量比例分摊,显示为负数
+            double offsetPointsPerItem = (order.getOffsetPoints() != null && order.getProductNums() > 0) 
+                ? Arith.div(Arith.div(Double.valueOf(order.getOffsetPoints()), 100), order.getProductNums()) * item.getProdCount() : 0;
+            ExportUtils.createCell(sheet, row, 16, 0, rowNum, "-" + offsetPointsPerItem, numberStyle);
 
-                // 单价(元)
-                ExportUtils.createCell(sheet, currentRow, 8, 0, rowNum, item.getPrice(), numberStyle);
+            // 实付金额 - 按商品数量比例分摊
+            double actualTotalPerItem = order.getProductNums() > 0 ? Arith.div(order.getActualTotal(), order.getProductNums()) * item.getProdCount() : 0;
+            ExportUtils.createCell(sheet, row, 17, 0, rowNum, actualTotalPerItem, numberStyle);
 
-                // 数量
-                ExportUtils.createCell(sheet, currentRow, 9, 0, rowNum, item.getProdCount(), dataStyle);
+            // 所属企业
+            ExportUtils.createCell(sheet, row, 18, 0, rowNum, order.getChannelName(), dataStyle);
 
-                // 小计(元)
-                ExportUtils.createCell(sheet, currentRow, 10, 0, rowNum, item.getProductTotalAmount(), numberStyle);
+            // 买家姓名
+            ExportUtils.createCell(sheet, row, 19, 0, rowNum, order.getReceiver(), dataStyle);
 
-            }
+            // 买家电话
+            ExportUtils.createCell(sheet, row, 20, 0, rowNum, order.getUserMobile(), dataStyle);
         }
 
         /**

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

@@ -43,7 +43,7 @@
         </foreach>
     </update>
     <select id="getListByOrderNumber" resultType="com.yami.shop.bean.model.OrderItem">
-        SELECT r.return_money_sts,sku.spec,oi.* FROM tz_order_item oi
+        SELECT r.return_money_sts,sku.spec,sku.hb_sku_id,sku.sku_code,oi.* FROM tz_order_item oi
         JOIN tz_order o ON o.order_number = oi.order_number  AND o.order_number = #{orderNumber}
         LEFT JOIN tz_order_refund r ON r.order_item_id = oi.order_item_id
         LEFT JOIN tz_sku sku on oi.sku_id=sku.sku_id