Przeglądaj źródła

v1.1.1部分功能开发

zhangxin 3 tygodni temu
rodzic
commit
3e3f43263c
24 zmienionych plików z 1178 dodań i 6 usunięć
  1. 6 0
      yami-shop-bean/src/main/java/com/yami/shop/bean/model/SysUser.java
  2. 7 0
      yami-shop-bean/src/main/java/com/yami/shop/bean/param/BackendOrderParam.java
  3. 5 0
      yami-shop-bean/src/main/java/com/yami/shop/bean/param/OrderRefundStaisticsParam.java
  4. 4 0
      yami-shop-bean/src/main/java/com/yami/shop/bean/vo/OrderRefundVo.java
  5. 17 0
      yami-shop-common/src/main/java/com/yami/shop/common/util/Arith.java
  6. 14 0
      yami-shop-platform/src/main/java/com/yami/shop/platform/controller/ChannelController.java
  7. 17 0
      yami-shop-platform/src/main/java/com/yami/shop/platform/controller/OrderController.java
  8. 20 0
      yami-shop-platform/src/main/java/com/yami/shop/platform/controller/OrderRefundController.java
  9. 2 0
      yami-shop-service/src/main/java/com/yami/shop/dao/OrderMapper.java
  10. 2 0
      yami-shop-service/src/main/java/com/yami/shop/dao/OrderRefundMapper.java
  11. 3 0
      yami-shop-service/src/main/java/com/yami/shop/service/OrderRefundService.java
  12. 2 0
      yami-shop-service/src/main/java/com/yami/shop/service/OrderService.java
  13. 245 1
      yami-shop-service/src/main/java/com/yami/shop/service/impl/OrderRefundServiceImpl.java
  14. 237 1
      yami-shop-service/src/main/java/com/yami/shop/service/impl/OrderServiceImpl.java
  15. 347 0
      yami-shop-service/src/main/java/com/yami/shop/utils/ExportUtils.java
  16. 65 0
      yami-shop-service/src/main/resources/mapper/OrderMapper.xml
  17. 47 0
      yami-shop-service/src/main/resources/mapper/OrderRefundMapper.xml
  18. 42 0
      yami-shop-sys/src/main/java/com/yami/shop/sys/dao/SysUserChannelMapper.java
  19. 3 0
      yami-shop-sys/src/main/java/com/yami/shop/sys/dao/SysUserMapper.java
  20. 39 0
      yami-shop-sys/src/main/java/com/yami/shop/sys/model/SysUserChannel.java
  21. 5 0
      yami-shop-sys/src/main/java/com/yami/shop/sys/service/SysUserService.java
  22. 21 4
      yami-shop-sys/src/main/java/com/yami/shop/sys/service/impl/SysUserServiceImpl.java
  23. 23 0
      yami-shop-sys/src/main/resources/mapper/SysUserChannelMapper.xml
  24. 5 0
      yami-shop-sys/src/main/resources/mapper/SysUserMapper.xml

+ 6 - 0
yami-shop-bean/src/main/java/com/yami/shop/bean/model/SysUser.java

@@ -81,6 +81,12 @@ public class SysUser implements Serializable {
 	@TableField(exist=false)
 	private List<Long> roleIdList;
 
+
+	/**
+	 * 企业ID列表
+	 */
+	@TableField(exist=false)
+	private List<Long> channelIdList;
 	/**
 	 * 部门列表
 	 */

+ 7 - 0
yami-shop-bean/src/main/java/com/yami/shop/bean/param/BackendOrderParam.java

@@ -10,11 +10,13 @@
 
 package com.yami.shop.bean.param;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import org.springframework.format.annotation.DateTimeFormat;
 
 import java.util.Date;
+import java.util.List;
 
 @Data
 public class BackendOrderParam {
@@ -75,4 +77,9 @@ public class BackendOrderParam {
      */
     private Integer refundStatus;
 
+    /**
+     * 企业ID列表
+     */
+    private List<Long> channelIdList;
+
 }

+ 5 - 0
yami-shop-bean/src/main/java/com/yami/shop/bean/param/OrderRefundStaisticsParam.java

@@ -15,6 +15,7 @@ import lombok.Data;
 import org.springframework.format.annotation.DateTimeFormat;
 
 import java.util.Date;
+import java.util.List;
 
 @Data
 public class OrderRefundStaisticsParam {
@@ -58,5 +59,9 @@ public class OrderRefundStaisticsParam {
     @ApiModelProperty("退款单状态 10:待审核 20:处理中 30:驳回退款 40:撤销退款 60:待退货(一审同意) 65:待确认收货(二审待审核) 70:退款完成")
     private Integer returnMoneySts;
 
+    /**
+     * 企业ID列表
+     */
+    private List<Long> channelIdList;
 
 }

+ 4 - 0
yami-shop-bean/src/main/java/com/yami/shop/bean/vo/OrderRefundVo.java

@@ -35,6 +35,10 @@ public class OrderRefundVo implements Serializable{
      * 退款编号
      */
     private String refundSn;
+    /**
+     * 所属企业
+     */
+    private String channelName;
     /**
      * 店铺ID
      */

+ 17 - 0
yami-shop-common/src/main/java/com/yami/shop/common/util/Arith.java

@@ -11,6 +11,7 @@
 package com.yami.shop.common.util;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.util.Objects;
 
 public class Arith {
@@ -93,6 +94,22 @@ public class Arith {
         return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
     }
 
+    /**
+     * 提供精度获取int类型*double类型
+     * @param v1
+     * @param v2
+     * @param scale
+     * @return
+     */
+    public static double multiply(int v1, double v2, int scale) {
+        if (scale < 0) {
+            throw new IllegalArgumentException("The scale must be a positive integer or zero");
+        }
+        BigDecimal b1 = new BigDecimal(Double.toString(v1));
+        BigDecimal b2 = new BigDecimal(Double.toString(v2));
+        return b1.multiply(b2).setScale(scale, RoundingMode.HALF_UP).doubleValue();
+    }
+
     /**
      * 提供精确的小数位四舍五入处理。.
      *

+ 14 - 0
yami-shop-platform/src/main/java/com/yami/shop/platform/controller/ChannelController.java

@@ -7,11 +7,14 @@ import com.yami.shop.bean.model.Channel;
 import com.yami.shop.bean.vo.ChannelVO;
 import com.yami.shop.common.util.PageParam;
 import com.yami.shop.common.util.R;
+import com.yami.shop.security.platform.util.SecurityUtils;
 import com.yami.shop.service.ChannelService;
 import com.yami.shop.service.UserService;
+import com.yami.shop.sys.service.SysUserService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.Date;
@@ -30,6 +33,9 @@ public class ChannelController {
     private final ChannelService channelService;
     private final UserService userService;
 
+    @Autowired
+    private SysUserService sysUserService;
+
     /**
      * 查询全部
      */
@@ -92,4 +98,12 @@ public class ChannelController {
         channelService.updateById(channel);
         return R.SUCCESS();
     }
+
+    @GetMapping("/list")
+    @ApiOperation(value = "查询全部")
+    public R<List<Channel>> listByUserId() {
+        Long userId = SecurityUtils.getSysUser().getUserId();
+        List<Channel> channels = sysUserService.findByUserId(userId);
+        return R.SUCCESS(channels);
+    }
 }

+ 17 - 0
yami-shop-platform/src/main/java/com/yami/shop/platform/controller/OrderController.java

@@ -75,6 +75,9 @@ public class OrderController {
     @GetMapping("/deliverList")
     @ApiOperation("后管端-快递订单列表")
     public R<IPage<Order>> deliverList(BackendOrderParam orderParam, PageParam<Order> page) {
+        if (orderParam.getChannelIdList()==null||orderParam.getChannelIdList().isEmpty()){
+            return R.FAIL("请求参数-所属企业不允许为空");
+        }
         IPage<Order> orderIPage = orderService.deliverList(page, orderParam);
         return R.SUCCESS(orderIPage);
     }
@@ -224,4 +227,18 @@ public class OrderController {
                 .collect(Collectors.joining(" "));
     }
 
+    /**
+     * 导出
+     * @param orderParam
+     * @param response
+     */
+    @GetMapping("/export")
+    @ApiOperation("后管端-快递订单列表导出")
+    public void export(BackendOrderParam orderParam, HttpServletResponse response) {
+        if (orderParam.getChannelIdList()==null||orderParam.getChannelIdList().isEmpty()){
+           throw new GlobalException("请求参数-所属企业不允许为空");
+        }
+         orderService.export(orderParam,response);
+    }
+
 }

+ 20 - 0
yami-shop-platform/src/main/java/com/yami/shop/platform/controller/OrderRefundController.java

@@ -8,10 +8,12 @@ import com.yami.shop.bean.model.OrderRefund;
 import com.yami.shop.bean.model.OrderRefundRecord;
 import com.yami.shop.bean.model.RefundAppointment;
 import com.yami.shop.bean.model.RefundDelivery;
+import com.yami.shop.bean.param.BackendOrderParam;
 import com.yami.shop.bean.param.OrderRefundCountParam;
 import com.yami.shop.bean.param.OrderRefundStaisticsParam;
 import com.yami.shop.bean.vo.OrderRefundSkuVo;
 import com.yami.shop.bean.vo.OrderRefundVo;
+import com.yami.shop.common.exception.GlobalException;
 import com.yami.shop.common.util.PageParam;
 import com.yami.shop.common.util.R;
 import com.yami.shop.dao.*;
@@ -22,6 +24,7 @@ import lombok.AllArgsConstructor;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
 import java.util.List;
 
 /**
@@ -110,6 +113,9 @@ public class OrderRefundController {
     @ApiOperation("后台分页获取退款订单列表")
     @GetMapping("/orderRefundPage")
     public R<IPage<OrderRefundVo>> orderRefundPage(PageParam<OrderRefund> page, OrderRefundStaisticsParam orderRefund) {
+        if (orderRefund.getChannelIdList()==null||orderRefund.getChannelIdList().isEmpty()){
+            return R.FAIL("请求参数-所属企业不允许为空");
+        }
         IPage<OrderRefundVo> page1 = orderRefundService.orderRefundPage(page, orderRefund);
         for (OrderRefundVo record : page1.getRecords()) {
             record.setOrderRefundSkuList(orderRefundskuMapper.selectByRefundId(record.getRefundId()));
@@ -126,4 +132,18 @@ public class OrderRefundController {
         return R.SUCCESS(orderRefundService.orderRefundCount(orderRefund));
     }
 
+    /**
+     * 导出
+     * @param orderRefund
+     * @param response
+     */
+    @GetMapping("/export")
+    @ApiOperation("后管端-快递订单列表导出")
+    public void export(OrderRefundStaisticsParam orderRefund, HttpServletResponse response) {
+//        if (orderRefund.getChannelIdList()==null||orderRefund.getChannelIdList().isEmpty()){
+//            throw new GlobalException("请求参数-所属企业不允许为空");
+//        }
+        orderRefundService.export(orderRefund,response);
+    }
+
 }

+ 2 - 0
yami-shop-service/src/main/java/com/yami/shop/dao/OrderMapper.java

@@ -139,4 +139,6 @@ public interface OrderMapper extends BaseMapper<Order> {
      * 后管端分页获取订单列表
      */
     IPage<Order> deliveryOrder(@Param("page") PageParam<Order> page, @Param("orderParam") OrderStatisticsParam orderParam);
+
+    List<Order> findList( @Param("orderParam")BackendOrderParam orderParam);
 }

+ 2 - 0
yami-shop-service/src/main/java/com/yami/shop/dao/OrderRefundMapper.java

@@ -106,4 +106,6 @@ public interface OrderRefundMapper extends BaseMapper<OrderRefund> {
     OrderRefundVo selectInfoById(Long refundId);
 
     List<OrderRefund> findByOrderNumber(@Param("orderNumber")String orderNumber, @Param("returnMoneySts")Integer returnMoneySts);
+
+    List<OrderRefundVo> findOrderRefund( @Param("orderRefund") OrderRefundStaisticsParam orderRefund);
 }

+ 3 - 0
yami-shop-service/src/main/java/com/yami/shop/service/OrderRefundService.java

@@ -18,6 +18,7 @@ import com.yami.shop.common.util.PageParam;
 import com.yami.shop.common.util.hb.HBR;
 import org.apache.ibatis.annotations.Param;
 
+import javax.servlet.http.HttpServletResponse;
 import java.util.Date;
 import java.util.List;
 
@@ -217,4 +218,6 @@ public interface OrderRefundService extends IService<OrderRefund> {
      * @param refundId  id
      */
     OrderRefundVo selectInfoById(Long refundId);
+
+    void export(OrderRefundStaisticsParam orderRefund, HttpServletResponse response);
 }

+ 2 - 0
yami-shop-service/src/main/java/com/yami/shop/service/OrderService.java

@@ -176,4 +176,6 @@ public interface OrderService extends IService<Order> {
      * @return 订单发货列表
      */
     IPage<Order> deliveryOrder(PageParam<Order> page, OrderStatisticsParam orderParam);
+
+    void export(BackendOrderParam orderParam, HttpServletResponse response);
 }

+ 245 - 1
yami-shop-service/src/main/java/com/yami/shop/service/impl/OrderRefundServiceImpl.java

@@ -23,6 +23,7 @@ import com.yami.shop.bean.event.ReceiptOrderEvent;
 import com.yami.shop.bean.model.*;
 import com.yami.shop.bean.param.*;
 import com.yami.shop.bean.pay.RefundInfoDto;
+import com.yami.shop.bean.vo.OrderRefundSkuVo;
 import com.yami.shop.bean.vo.OrderRefundVo;
 import com.yami.shop.common.config.Constant;
 import com.yami.shop.common.enums.PayType;
@@ -34,15 +35,22 @@ import com.yami.shop.common.util.hb.HBR;
 import com.yami.shop.dao.*;
 import com.yami.shop.service.*;
 import com.yami.shop.service.hb.impl.HBOrderService;
+import com.yami.shop.utils.ExportUtils;
 import com.yami.shop.utils.HBSignUtil;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import ma.glasnost.orika.MapperFacade;
+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.xssf.usermodel.XSSFWorkbook;
 import org.springframework.context.ApplicationContext;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
+import javax.servlet.http.HttpServletResponse;
 import java.math.BigDecimal;
 import java.util.*;
 
@@ -56,7 +64,7 @@ public class OrderRefundServiceImpl extends ServiceImpl<OrderRefundMapper, Order
     private final OrderRefundMapper orderRefundMapper;
 
     private final OrderRefundRecordMapper orderRefundRecordMapper;
-
+    private final OrderRefundSkuMapper orderRefundskuMapper;
     private final OrderService orderService;
     private final ProductService productService;
     private final SkuService skuService;
@@ -1294,6 +1302,242 @@ public class OrderRefundServiceImpl extends ServiceImpl<OrderRefundMapper, Order
     public OrderRefundVo selectInfoById(Long refundId) {
         return orderRefundMapper.selectInfoById(refundId);
     }
+    /**
+     * 导出正常订单
+     * @param orderRefund
+     * @param response
+     */
+    @Override
+    public void export(OrderRefundStaisticsParam orderRefund, HttpServletResponse response) {
+
+        List<OrderRefundVo> orderRefundList= orderRefundMapper.findOrderRefund(orderRefund);
+        for (OrderRefundVo record : orderRefundList) {
+            record.setOrderRefundSkuList(orderRefundskuMapper.selectByRefundId(record.getRefundId()));
+        }
+        if (orderRefundList.isEmpty()){
+            throw new GlobalException("该次导出未查询到数据,不允许导出");
+        }
+        exportNormalOrders(orderRefundList,response,"退款订单");
+    }
+
+    /**
+     * 导出正常订单数据到Excel(直接输出到HttpServletResponse)
+     */
+    public void exportNormalOrders(List<OrderRefundVo> orderList,
+                                   HttpServletResponse response,
+                                   String fileName) {
+        // 设置响应头
+        ExportUtils.setupResponse(response, fileName);
+
+        try (Workbook workbook = new XSSFWorkbook()) {
+            // 创建Sheet
+            Sheet sheet = workbook.createSheet("退款订单");
+
+            // 设置列宽
+            setupColumnWidth(sheet);
+
+            // 创建表头样式
+            CellStyle headerStyle = ExportUtils.createHeaderStyle(workbook);
+
+            // 创建数据样式
+            CellStyle dataStyle = ExportUtils.createDataStyle(workbook);
+            CellStyle numberStyle = ExportUtils.createNumberStyle(workbook);
+            CellStyle dateStyle = ExportUtils.createDateStyle(workbook);
+
+
+            String[] headers = {
+                    "序号","订单编号", "退款编号", "申请时间", "申请方式", "售后状态",
+                    "订单类型", "退款商品名称", "规格", "购买单价(元)","退款数量", "小计(元)",
+                    "退款商品总数量", "退款总金额(元)", "退还现金(元)", "退还积分(元)","退还积分中过期积分(分)",
+                    "所属企业", "买家姓名", "买家电话"
+            };
+            // 创建合并单元格的表头
+            ExportUtils.createMergedHeader(sheet, headerStyle,"退款订单");
+
+            // 创建列标题行
+            ExportUtils.createColumnHeaders(sheet, headerStyle,headers);
+
+            // 填充数据
+            fillOrderData(sheet, orderList, dataStyle, numberStyle, dateStyle);
+
+            // 写入响应流
+            workbook.write(response.getOutputStream());
+            response.getOutputStream().flush();
+
+            log.info("订单导出成功,文件:{},记录数:{}", fileName, orderList.size());
+
+        } catch (Exception e) {
+            log.error("订单导出失败", e);
+            throw new RuntimeException("导出失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 设置列宽
+     */
+    private void setupColumnWidth(Sheet sheet) {
+        // 根据模板设置列宽(单位:1/256字符宽度)
+        sheet.setColumnWidth(0, 12 * 256);   // 序号
+        sheet.setColumnWidth(1, 12 * 256);   // 订单编号
+        sheet.setColumnWidth(2, 18 * 256);   // 退款编号
+        sheet.setColumnWidth(3, 18 * 256);   // 申请时间
+        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, 15 * 256);  // 所属企业
+        sheet.setColumnWidth(18, 10 * 256);  // 买家姓名
+        sheet.setColumnWidth(19, 15 * 256);  // 买家电话
+    }
+
+    /**
+     * 填充订单数据
+     */
+    private void fillOrderData(Sheet sheet, List<OrderRefundVo> orderList,
+                               CellStyle dataStyle, CellStyle numberStyle, CellStyle dateStyle) {
+        int rowNum = 2; // 从第3行开始(0-based索引)
+        int indexNum= 1; //序号
+        for (OrderRefundVo orderRefundVo : orderList) {
+            Row row = sheet.createRow(rowNum++);
+            row.setHeightInPoints(18);
+            // 序号
+            ExportUtils.createCell(sheet,row, 0,orderRefundVo.getOrderRefundSkuList().size(),rowNum, indexNum, dataStyle);
+            fillOrderRowData(sheet,rowNum,row, orderRefundVo, dataStyle, numberStyle, dateStyle);
+            rowNum+=orderRefundVo.getOrderRefundSkuList().size()-1;
+            indexNum++;
+        }
+
+    }
+
+    /**
+     * 填充单行订单数据
+     */
+    private void fillOrderRowData(Sheet sheet,int rowNum,Row row, OrderRefundVo orderRefundVo,
+                                  CellStyle dataStyle, CellStyle numberStyle, CellStyle dateStyle) {
+
+        // 订单编号
+        ExportUtils.createCell(sheet,row, 1,orderRefundVo.getOrderRefundSkuList().size(),rowNum, orderRefundVo.getOrderNumber(), dataStyle);
+
+        // 退款编号
+        ExportUtils.createCell(sheet,row, 2,orderRefundVo.getOrderRefundSkuList().size(),rowNum,  orderRefundVo.getRefundSn(), dateStyle);
+
+        // 申请时间
+        ExportUtils.createCell(sheet,row, 3,orderRefundVo.getOrderRefundSkuList().size(),rowNum,  ExportUtils.formatDate(orderRefundVo.getApplyTime()), dateStyle);
+
+        // 申请方式
+        ExportUtils.createCell(sheet,row, 4,orderRefundVo.getOrderRefundSkuList().size(),rowNum,getApplyType(orderRefundVo.getApplyType()), dataStyle);
+
+        // 售后状态
+        ExportUtils.createCell(sheet,row, 5,orderRefundVo.getOrderRefundSkuList().size(),rowNum, getReturnMoneySts(orderRefundVo.getReturnMoneySts()), dataStyle);
+        // 订单类型
+        ExportUtils.createCell(sheet,row, 6,orderRefundVo.getOrderRefundSkuList().size(),rowNum, getDvyType(orderRefundVo.getDvyType()), dataStyle);
+        // 商品明细行(每个订单项一行)
+        fillOrderItems(sheet, rowNum, row, orderRefundVo, dataStyle, numberStyle);
+
+        // 退款商品总数量
+        ExportUtils.createCell(sheet,row, 12,orderRefundVo.getOrderRefundSkuList().size(),rowNum, orderRefundVo.getGoodsNum(), numberStyle);
+
+        // 退款总金额(元)
+        ExportUtils.createCell(sheet,row, 13,orderRefundVo.getOrderRefundSkuList().size(),rowNum, orderRefundVo.getRefundTotalMoney(), numberStyle);
+
+        // 退还现金(元)
+        ExportUtils.createCell(sheet,row, 14,orderRefundVo.getOrderRefundSkuList().size(),rowNum, orderRefundVo.getRefundAmount(), numberStyle);
+
+        // 退还积分(元)
+        ExportUtils.createCell(sheet,row,15,orderRefundVo.getOrderRefundSkuList().size(),rowNum,"-"+(orderRefundVo.getOffsetPoints()==null?0:Arith.div(Double.valueOf(orderRefundVo.getOffsetPoints()), 100)), numberStyle);
+
+        // 退还积分中过期积分(分)
+        ExportUtils.createCell(sheet,row, 16,orderRefundVo.getOrderRefundSkuList().size(),rowNum, (orderRefundVo.getRefundExpiredScore()==null?0:orderRefundVo.getRefundExpiredScore()), numberStyle);
+
+        // 所属企业
+        ExportUtils.createCell(sheet,row, 17,orderRefundVo.getOrderRefundSkuList().size(),rowNum, orderRefundVo.getChannelName(), dataStyle);
+
+        // 买家姓名
+        ExportUtils.createCell(sheet,row, 18,orderRefundVo.getOrderRefundSkuList().size(),rowNum, orderRefundVo.getReceiver(), dataStyle);
+
+        // 买家电话
+        ExportUtils.createCell(sheet,row, 19,orderRefundVo.getOrderRefundSkuList().size(),rowNum, orderRefundVo.getBuyerMobile(), dataStyle);
+    }
+    /**
+     * 填充订单项明细
+     */
+    private void fillOrderItems(Sheet sheet, int rowNum, Row row, OrderRefundVo orderRefundVo,
+                                CellStyle dataStyle, CellStyle numberStyle) {
+        int itemSize = orderRefundVo.getOrderRefundSkuList().size();
+
+        for (int i = 0; i < itemSize; i++) {
+            Row currentRow = (i < 1) ? row : sheet.createRow(rowNum + (i-1));
+            OrderRefundSkuVo orderRefundSkuVo = orderRefundVo.getOrderRefundSkuList().get(i);
+            // 退款商品名称
+            ExportUtils.createCell(sheet, currentRow, 7, 0, rowNum, orderRefundSkuVo.getProdName(), dataStyle);
+
+            // 规格
+            ExportUtils.createCell(sheet, currentRow, 8, 0, rowNum, orderRefundSkuVo.getSpec(), dataStyle);
+
+            // 购买单价(元)
+            ExportUtils.createCell(sheet, currentRow, 9, 0, rowNum, orderRefundSkuVo.getSkuPrice(), numberStyle);
+
+            // 退款数量
+            ExportUtils.createCell(sheet, currentRow, 10, 0, rowNum, orderRefundSkuVo.getProductCount(), dataStyle);
+
+            // 小计(元)
+            ExportUtils.createCell(sheet, currentRow, 11, 0, rowNum,(Arith.multiply(orderRefundSkuVo.getProductCount(),orderRefundSkuVo.getSkuPrice(),2)), numberStyle);
+
+        }
+    }
+
+    /**
+     *海博订单状态
+     * @param status
+     * @return
+     */
+    private String getReturnMoneySts(Integer status) {
+        switch (status) {
+            case 10: return "待审核";
+            case 20: return "处理中";
+            case 30: return "驳回退款";
+            case 40: return "撤销退款";
+            case 60: return "待退货(一审同意)";
+            case 65: return "待确认收货(二审待审核)";
+            case 70: return "退款完成";
+            default: return "未知状态";
+        }
+    }
+    /**
+     * 订单类型
+     * @param dvyType
+     * @return
+     */
+    private String getDvyType(Integer dvyType) {
+        switch (dvyType) {
+            case 1: return "快递";
+            case 2: return "自提";
+            case 3: return "及时配送";
+            default: return "未知类型";
+        }
+    }
+    /**
+     * 订单类型
+     * @param applyType
+     * @return
+     */
+    private String getApplyType(Integer applyType) {
+        switch (applyType) {
+            case 1: return "仅退款";
+            case 2: return "退货退款";
+            case 5: return "差价退款";
+            default: return "未知类型";
+        }
+    }
 
 
 }

+ 237 - 1
yami-shop-service/src/main/java/com/yami/shop/service/impl/OrderServiceImpl.java

@@ -18,8 +18,11 @@ import cn.hutool.core.lang.Snowflake;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.RandomUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
 import cn.hutool.poi.excel.ExcelUtil;
 import cn.hutool.poi.excel.ExcelWriter;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -48,13 +51,16 @@ import com.yami.shop.service.OrderItemService;
 import com.yami.shop.service.OrderService;
 import com.yami.shop.service.OrderSettlementService;
 import com.yami.shop.utils.CullenUtils;
+import com.yami.shop.utils.ExportUtils;
 import com.yami.shop.wx.po.RefundInfoPo;
 import com.yami.shop.wx.service.WxProviderService;
 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.Sheet;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.springframework.beans.BeanUtils;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.CachePut;
@@ -67,6 +73,7 @@ import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.math.BigDecimal;
+import java.net.URLEncoder;
 import java.text.SimpleDateFormat;
 import java.time.Instant;
 import java.time.LocalDateTime;
@@ -1295,4 +1302,233 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
         // 4. 比较时间
         return nowInstant.isAfter(expirationInstant) || nowInstant.equals(expirationInstant);
     }
+
+    /**
+     * 导出正常订单
+     * @param orderParam
+     * @param response
+     */
+    @Override
+    public void export(BackendOrderParam orderParam, HttpServletResponse response) {
+        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()){
+            throw new GlobalException("该次导出未查询到数据,不允许导出");
+        }
+        exportNormalOrders(orderList,response,"正常订单");
+    }
+
+    /**
+     * 导出正常订单数据到Excel(直接输出到HttpServletResponse)
+     */
+    public void exportNormalOrders(List<Order> orderList,
+                                   HttpServletResponse response,
+                                   String fileName) {
+        // 设置响应头
+        ExportUtils.setupResponse(response, fileName);
+
+        try (Workbook workbook = new XSSFWorkbook()) {
+            // 创建Sheet
+            Sheet sheet = workbook.createSheet("正常订单");
+
+            // 设置列宽
+            setupColumnWidth(sheet);
+
+            // 创建表头样式
+            CellStyle headerStyle = ExportUtils.createHeaderStyle(workbook);
+
+            // 创建数据样式
+            CellStyle dataStyle = ExportUtils.createDataStyle(workbook);
+            CellStyle numberStyle = ExportUtils.createNumberStyle(workbook);
+            CellStyle dateStyle = ExportUtils.createDateStyle(workbook);
+
+
+            String[] headers = {
+                "序号","订单编号", "下单时间", "付款时间", "订单状态", "订单类型",
+                "商品名称", "规格", "单价(元)", "数量", "小计(元)",
+                "商品总数量", "商品总额(元)", "运费(元)", "积分抵扣(元)",
+                "实付金额", "所属企业", "买家姓名", "买家电话"
+            };
+            // 创建合并单元格的表头
+            ExportUtils.createMergedHeader(sheet, headerStyle,"正常订单");
+
+            // 创建列标题行
+            ExportUtils.createColumnHeaders(sheet, headerStyle,headers);
+
+            // 填充数据
+            fillOrderData(sheet, orderList, dataStyle, numberStyle, dateStyle);
+
+            // 写入响应流
+            workbook.write(response.getOutputStream());
+            response.getOutputStream().flush();
+
+            log.info("订单导出成功,文件:{},记录数:{}", fileName, orderList.size());
+
+        } catch (Exception e) {
+            log.error("订单导出失败", e);
+            throw new RuntimeException("导出失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 设置列宽
+     */
+    private void setupColumnWidth(Sheet sheet) {
+        // 根据模板设置列宽(单位:1/256字符宽度)
+        sheet.setColumnWidth(0, 12 * 256);   // 序号
+        sheet.setColumnWidth(1, 12 * 256);   // 订单编号
+        sheet.setColumnWidth(2, 18 * 256);   // 下单时间
+        sheet.setColumnWidth(3, 18 * 256);   // 付款时间
+        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);  // 买家电话
+    }
+
+    /**
+     * 填充订单数据
+     */
+    private void fillOrderData(Sheet sheet, List<Order> orderList,
+                               CellStyle dataStyle, CellStyle numberStyle, CellStyle dateStyle) {
+        int rowNum = 2; // 从第3行开始(0-based索引)
+        int indexNum= 1; //序号
+        for (Order order : orderList) {
+            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++;
+        }
+
+    }
+
+    /**
+     * 填充单行订单数据
+     */
+    private void fillOrderRowData(Sheet sheet,int rowNum,Row row, Order order,
+                                  CellStyle dataStyle, CellStyle numberStyle, CellStyle dateStyle) {
+
+        // 订单编号
+        ExportUtils.createCell(sheet,row, 1,order.getOrderItems().size(),rowNum, order.getOrderNumber(), dataStyle);
+
+        // 下单时间
+        ExportUtils.createCell(sheet,row, 2,order.getOrderItems().size(),rowNum,  ExportUtils.formatDate(order.getCreateTime()), dateStyle);
+
+        // 付款时间
+        ExportUtils.createCell(sheet,row, 3,order.getOrderItems().size(),rowNum,  ExportUtils.formatDate(order.getPayTime()), dateStyle);
+
+        // 订单状态
+        ExportUtils.createCell(sheet,row, 4,order.getOrderItems().size(),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, 11,order.getOrderItems().size(),rowNum, order.getProductNums(), dataStyle);
+
+        // 商品总额(元)
+        ExportUtils.createCell(sheet,row, 12,order.getOrderItems().size(),rowNum, order.getTotal(), numberStyle);
+
+        // 运费(元)
+        ExportUtils.createCell(sheet,row, 13,order.getOrderItems().size(),rowNum, order.getFreightAmount(), numberStyle);
+
+        // 积分抵扣(元) - 显示为负数
+        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, 15,order.getOrderItems().size(),rowNum, order.getActualTotal(), numberStyle);
+
+        // 所属企业
+        ExportUtils.createCell(sheet,row, 16,order.getOrderItems().size(),rowNum, order.getChannelName(), dataStyle);
+
+        // 买家姓名
+        ExportUtils.createCell(sheet,row, 17,order.getOrderItems().size(),rowNum, order.getReceiver(), dataStyle);
+
+        // 买家电话
+        ExportUtils.createCell(sheet,row, 18,order.getOrderItems().size(),rowNum, order.getUserMobile(), 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, currentRow, 6, 0, rowNum, item.getProdName(), dataStyle);
+
+            // 规格
+            ExportUtils.createCell(sheet, currentRow, 7, 0, rowNum, item.getSpec(), dataStyle);
+
+            // 单价(元)
+            ExportUtils.createCell(sheet, currentRow, 8, 0, rowNum, item.getPrice(), numberStyle);
+
+            // 数量
+            ExportUtils.createCell(sheet, currentRow, 9, 0, rowNum, item.getProdCount(), dataStyle);
+
+            // 小计(元)
+            ExportUtils.createCell(sheet, currentRow, 10, 0, rowNum, item.getProductTotalAmount(), numberStyle);
+
+        }
+    }
+
+    /**
+     *海博订单状态
+     * @param status
+     * @return
+     */
+    private String getHbOrderStatus(Integer status) {
+        switch (status) {
+            case 0: return "待支付";
+            case 1: return "待发货";
+            case 20: return "待收货";
+            case 30: return "待收货";
+            case 40: return "待收货";
+            case 50: return "订单取消待审核";
+            case 60: return "订单取消";
+            case 70: return "待收货";
+            case 80: return "订单完成";
+            default: return "未知状态";
+        }
+    }
+
+    /**
+     * 订单类型
+     * @param dvyType
+     * @return
+     */
+    private String getDvyType(Integer dvyType) {
+        switch (dvyType) {
+            case 1: return "快递";
+            case 2: return "自提";
+            case 3: return "及时配送";
+            default: return "未知类型";
+        }
+    }
+
 }

+ 347 - 0
yami-shop-service/src/main/java/com/yami/shop/utils/ExportUtils.java

@@ -0,0 +1,347 @@
+package com.yami.shop.utils;
+
+import com.google.common.collect.Maps;
+import com.yami.shop.common.exception.GlobalException;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+import javax.servlet.http.HttpServletResponse;
+import java.math.BigDecimal;
+import java.net.URLEncoder;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * 导出工具类
+ * @author zx
+ * @date 2015-12-12 00:00:00
+ */
+public class ExportUtils {
+
+    /**
+     * 创建合并单元格的表头(模拟模板中的"正常订单"标题)
+     */
+    public static void createMergedHeader(Sheet sheet, CellStyle headerStyle,String headerName) {
+        // 第一行:主标题
+        Row titleRow = sheet.createRow(0);
+        Cell titleCell = titleRow.createCell(0);
+        titleCell.setCellValue(headerName);
+        titleCell.setCellStyle(headerStyle);
+
+        // 合并A1到R1单元格
+        sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 17));
+
+        // 设置行高
+        titleRow.setHeightInPoints(25);
+    }
+
+    /**
+     * 设置HTTP响应头
+     */
+    public static void setupResponse(HttpServletResponse response, String fileName) {
+        try {
+            String encodedFileName = URLEncoder.encode(fileName, "UTF-8")
+                    .replaceAll("\\+", "%20");
+
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("UTF-8");
+            response.setHeader("Content-Disposition",
+                    "attachment; filename*=UTF-8''" + encodedFileName + ".xlsx");
+            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
+            response.setHeader("Pragma", "no-cache");
+            response.setHeader("Cache-Control", "no-cache");
+
+        } catch (Exception e) {
+            throw new RuntimeException("设置响应头失败", e);
+        }
+    }
+    /**
+     * 创建列标题行
+     */
+    public static  void createColumnHeaders(Sheet sheet, CellStyle headerStyle,String[] headers) {
+        Row headerRow = sheet.createRow(1);
+        headerRow.setHeightInPoints(20);
+
+//        String[] headers = {
+//                "订单编号", "下单时间", "付款时间", "订单状态", "订单类型",
+//                "商品名称", "规格", "单价(元)", "数量", "小计(元)",
+//                "商品总数量", "商品总额(元)", "运费(元)", "积分抵扣(元)",
+//                "实付金额", "所属企业", "买家姓名", "买家电话"
+//        };
+
+        for (int i = 0; i < headers.length; i++) {
+            Cell cell = headerRow.createCell(i);
+            cell.setCellValue(headers[i]);
+            cell.setCellStyle(headerStyle);
+        }
+    }
+
+    /**
+     * 创建单元格(字符串类型)
+     */
+    public static void createCell(Sheet sheet,Row row,int col, int size,int rowNum, String value, CellStyle style) {
+        int lastRow = rowNum + size - 1;
+        if (lastRow > rowNum) {
+            if (!isMergeRegionOverlap(sheet, rowNum-1, lastRow-1, col)){ //因为索引是从0开始的  所以开始行和结束行都需要减1
+                CellRangeAddress cellAddresses = new CellRangeAddress(rowNum - 1, lastRow - 1, col, col);
+                sheet.addMergedRegion(cellAddresses);
+                setMergedRegionBorder(sheet,cellAddresses);
+            }
+        }
+        Cell cell = row.createCell(col);
+        cell.setCellValue(value != null ? value : "");
+        cell.setCellStyle(style);
+
+    }
+
+    /**
+     * 创建单元格(数字类型)
+     */
+    public static void createCell(Sheet sheet, Row row, int col, int size, int rowNum, BigDecimal value, CellStyle style) {
+        int lastRow = rowNum + size - 1;
+        if (lastRow > rowNum) {
+            if (!isMergeRegionOverlap(sheet, rowNum-1, lastRow-1, col)){
+                CellRangeAddress cellAddresses = new CellRangeAddress(rowNum - 1, lastRow - 1, col, col);
+                sheet.addMergedRegion(cellAddresses);
+                setMergedRegionBorder(sheet,cellAddresses);
+            }
+        }
+        Cell cell = row.createCell(col);
+        if (value != null) {
+            cell.setCellValue(value.doubleValue());
+        } else {
+            cell.setCellValue(0);
+        }
+        cell.setCellStyle(style);
+    }
+    /**
+     * 创建单元格(数字类型)
+     */
+    public static void createCell(Sheet sheet,Row row,int col, int size,int rowNum, double value, CellStyle style) {
+        int lastRow = rowNum + size - 1;
+        if (lastRow > rowNum) {
+            if (!isMergeRegionOverlap(sheet, rowNum-1, lastRow-1, col)){
+                CellRangeAddress cellAddresses = new CellRangeAddress(rowNum - 1, lastRow - 1, col, col);
+                sheet.addMergedRegion(cellAddresses);
+                setMergedRegionBorder(sheet,cellAddresses);
+            }
+        }
+        Cell cell = row.createCell(col);
+        cell.setCellValue(value);
+        cell.setCellStyle(style);
+    }
+
+    /**
+     * 创建单元格(整数类型)
+     */
+    public static void createCell(Sheet sheet,Row row,int col, int size,int rowNum,  Integer value, CellStyle style) {
+        int lastRow = rowNum + size - 1;
+        if (lastRow > rowNum) {
+            if (!isMergeRegionOverlap(sheet, rowNum-1, lastRow-1, col)){
+                CellRangeAddress cellAddresses = new CellRangeAddress(rowNum - 1, lastRow - 1, col, col);
+                sheet.addMergedRegion(cellAddresses);
+                setMergedRegionBorder(sheet,cellAddresses);
+            }
+        }
+        Cell cell = row.createCell(col);
+        if (value != null) {
+            cell.setCellValue(value);
+        } else {
+            cell.setCellValue(0);
+        }
+        cell.setCellStyle(style);
+    }
+
+    /**
+     * 创建表头样式
+     */
+    public static CellStyle createHeaderStyle(Workbook workbook) {
+        CellStyle style = workbook.createCellStyle();
+
+        // 字体设置
+        Font font = workbook.createFont();
+        font.setBold(true);
+        font.setFontHeightInPoints((short) 11);
+        font.setColor(IndexedColors.WHITE.getIndex());
+        style.setFont(font);
+
+        // 背景色 - 深蓝色
+        style.setFillForegroundColor(IndexedColors.DARK_BLUE.getIndex());
+        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+
+        // 对齐方式
+        style.setAlignment(HorizontalAlignment.CENTER);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+
+        // 边框
+        setBorderStyle(style);
+
+        return style;
+    }
+
+    /**
+     * 创建数据样式
+     */
+    public static CellStyle createDataStyle(Workbook workbook) {
+        CellStyle style = workbook.createCellStyle();
+
+        Font font = workbook.createFont();
+        font.setFontHeightInPoints((short) 10);
+        style.setFont(font);
+
+        style.setAlignment(HorizontalAlignment.LEFT);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+        setBorderStyle(style);
+
+        return style;
+    }
+
+    /**
+     * 创建数字样式
+     */
+    public static CellStyle createNumberStyle(Workbook workbook) {
+        CellStyle style = workbook.createCellStyle();
+
+        Font font = workbook.createFont();
+        font.setFontHeightInPoints((short) 10);
+        style.setFont(font);
+
+        style.setAlignment(HorizontalAlignment.RIGHT);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+
+        // 数字格式:保留2位小数
+        DataFormat format = workbook.createDataFormat();
+        style.setDataFormat(format.getFormat("0.00"));
+        setBorderStyle(style);
+
+        return style;
+    }
+
+    /**
+     * 创建日期样式
+     */
+    public static CellStyle createDateStyle(Workbook workbook) {
+        CellStyle style = workbook.createCellStyle();
+
+        Font font = workbook.createFont();
+        font.setFontHeightInPoints((short) 10);
+        style.setFont(font);
+
+        style.setAlignment(HorizontalAlignment.CENTER);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+
+        // 日期格式
+        DataFormat format = workbook.createDataFormat();
+        style.setDataFormat(format.getFormat("yyyy-MM-dd HH:mm"));
+        setBorderStyle(style);
+
+        return style;
+    }
+
+    /**
+     * 设置边框样式
+     */
+    public static void setBorderStyle(CellStyle style) {
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBorderTop(BorderStyle.THIN);
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setBorderRight(BorderStyle.THIN);
+        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+    }
+    /**
+     * 为合并区域设置边框样式
+     */
+    public static void setMergedRegionBorder(Sheet sheet, CellRangeAddress mergedRegion) {
+        int firstRow = mergedRegion.getFirstRow();
+        int lastRow = mergedRegion.getLastRow();
+        int firstCol = mergedRegion.getFirstColumn();
+        int lastCol = mergedRegion.getLastColumn();
+
+        // 为合并区域的每个单元格设置特定边框
+        for (int row = firstRow; row <= lastRow; row++) {
+            for (int col = firstCol; col <= lastCol; col++) {
+                Row sheetRow = sheet.getRow(row);
+                if (sheetRow == null) {
+                    sheetRow = sheet.createRow(row);
+                }
+                Cell cell = sheetRow.getCell(col);
+                if (cell == null) {
+                    cell = sheetRow.createCell(col);
+                }
+
+                CellStyle borderStyle = sheet.getWorkbook().createCellStyle();
+
+                // 设置左边框(只有最左侧单元格)
+                if (col == firstCol) {
+                    borderStyle.setBorderLeft(BorderStyle.THIN);
+                    borderStyle.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+                }
+
+                // 设置右边框(只有最右侧单元格)
+                if (col == lastCol) {
+                    borderStyle.setBorderRight(BorderStyle.THIN);
+                    borderStyle.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+                }
+
+                // 设置上边框(只有最上方单元格)
+                if (row == firstRow) {
+                    borderStyle.setBorderTop(BorderStyle.THIN);
+                    borderStyle.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+                }
+
+                // 设置下边框(只有最下方单元格)
+                if (row == lastRow) {
+                    borderStyle.setBorderBottom(BorderStyle.THIN);
+                    borderStyle.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+                }
+                // 如果是左上角单元格,设置居中
+                if (row == firstRow && col == firstCol) {
+                    setCenterAlignment(borderStyle);
+                    // 可选:设置自动换行
+//                    borderStyle.setWrapText(true);
+                }
+
+                cell.setCellStyle(borderStyle);
+            }
+        }
+    }
+    /**
+     * 设置居中对齐
+     */
+    public static void setCenterAlignment(CellStyle style) {
+        // 水平居中
+        style.setAlignment(HorizontalAlignment.CENTER);
+        // 垂直居中
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+    }
+    /**
+     * 格式化日期
+     */
+    public static String formatDate(Date date) {
+        if (date == null) return "";
+
+        // 使用java.time格式化(Java 8+)
+        return java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
+                .format(date.toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDateTime());
+    }
+    /**
+     * 检查合并区域是否重叠
+     */
+    public static boolean isMergeRegionOverlap(Sheet sheet, int startRow, int endRow, int column) {
+        for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
+            CellRangeAddress existingRegion = sheet.getMergedRegion(i);
+            if (existingRegion.getFirstColumn() == column &&
+                    existingRegion.getLastColumn() == column) {
+                // 检查行重叠
+                if (!(endRow < existingRegion.getFirstRow() || startRow > existingRegion.getLastRow())) {
+                    return true; // 存在重叠
+                }
+            }
+        }
+        return false;
+    }
+
+}

+ 65 - 0
yami-shop-service/src/main/resources/mapper/OrderMapper.xml

@@ -487,6 +487,12 @@
             <if test="orderParam.orderNumber != null and orderParam.orderNumber != ''">
                 and a.order_number = #{orderParam.orderNumber}
             </if>
+            <if test="orderParam.channelIdList != null and orderParam.channelIdList != ''">
+                and a.channel_id  in
+                <foreach collection="orderParam.channelIdList" item="channelId" open="(" close=")" separator=",">
+                    #{channelId}
+                </foreach>
+            </if>
             <if test="orderParam.shopId != null">
                 and a.shop_id = #{orderParam.shopId}
             </if>
@@ -1088,4 +1094,63 @@
         ORDER BY o.create_time DESC
     </select>
 
+    <select id="findList" resultMap="BaseResultMap">
+        SELECT a.*,b.receiver,b.mobile user_mobile,c.shop_name,d.channel_name FROM tz_order a
+        LEFT JOIN tz_user_addr_order b on a.addr_order_id=b.addr_order_id
+        LEFT JOIN tz_shop_detail c on a.shop_id=c.shop_id
+        LEFT JOIN tz_channel d on a.channel_id=d.id
+        <where>
+            <if test="orderParam.orderNumber != null and orderParam.orderNumber != ''">
+                and a.order_number = #{orderParam.orderNumber}
+            </if>
+            <if test="orderParam.channelIdList != null and orderParam.channelIdList != ''">
+                and a.channel_id  in
+                <foreach collection="orderParam.channelIdList" item="channelId" open="(" close=")" separator=",">
+                    #{channelId}
+                </foreach>
+            </if>
+            <if test="orderParam.shopId != null">
+                and a.shop_id = #{orderParam.shopId}
+            </if>
+            <if test="orderParam.startTime != null">
+                and a.create_time &gt; #{orderParam.startTime}
+            </if>
+            <if test="orderParam.endTime != null">
+                and a.create_time &lt; #{orderParam.endTime}
+            </if>
+            <if test="orderParam.refundStatus != null and orderParam.refundStatus != 0">
+                and a.refund_status = #{orderParam.refundStatus}
+            </if>
+            <if test="orderParam.receiver != null">
+                and b.receiver LIKE concat("%",#{orderParam.receiver},"%")
+            </if>
+            <if test="orderParam.userMobile != null">
+                and b.mobile LIKE concat("%",#{orderParam.userMobile},"%")
+            </if>
+
+            <if test="orderParam.orderStatus != null and orderParam.orderStatus != ''">
+                <if test="orderParam.orderStatus == 'all'">
+                    AND a.hb_order_status in (0, 1,20,30,40,50,60,70,80)
+                </if>
+                <if test="orderParam.orderStatus == 'paddingPay'">
+                    AND a.hb_order_status in (0)
+                </if>
+
+                <if test="orderParam.orderStatus == 'paddingShipped'">
+                    AND a.hb_order_status in (1)
+                </if>
+
+                <if test="orderParam.orderStatus == 'paddingReceived'">
+                    AND a.hb_order_status in (20,30,40,70)
+                </if>
+                <if test="orderParam.orderStatus == 'completed'">
+                    AND a.hb_order_status in (80)
+                </if>
+                <if test="orderParam.orderStatus == 'cancel'">
+                    AND a.hb_order_status in (50,60)
+                </if>
+            </if>
+        </where>
+        order by a.create_time desc,a.order_id desc
+    </select>
 </mapper>

+ 47 - 0
yami-shop-service/src/main/resources/mapper/OrderRefundMapper.xml

@@ -64,6 +64,8 @@
         <result column="decision_time" property="decisionTime"/>
         <result column="update_time" property="updateTime"/>
         <result column="reject_time" property="rejectTime"/>
+        <result column="channel_name" property="channelName"/>
+        <result column="real_name" property="receiver"/>
         <result column="refund_total_money" property="refundTotalMoney"/>
     </resultMap>
     <resultMap id="tagResultMap" type="com.yami.shop.bean.dto.OrderRefundDto">
@@ -482,6 +484,12 @@
             <if test="orderRefund.orderNumber != null">
                 and refund.order_number = #{orderRefund.orderNumber}
             </if>
+            <if test="orderRefund.channelIdList != null and orderRefund.channelIdList != ''">
+                and o.channel_id  in
+                <foreach collection="orderRefund.channelIdList" item="channelId" open="(" close=")" separator=",">
+                    #{channelId}
+                </foreach>
+            </if>
             <if test="orderRefund.refundSn != null">
                 and refund.refund_sn = #{orderRefund.refundSn}
             </if>
@@ -523,5 +531,44 @@
     <select id="findByOrderNumber" resultType="com.yami.shop.bean.model.OrderRefund">
         select * from tz_order_refund where return_money_sts=#{returnMoneySts} and order_number =#{orderNumber}
     </select>
+    <select id="findOrderRefund" resultMap="OrderRefundVoMap">
+        select refund.*, o.actual_total as actualTotal,o.dvy_type as o_dvy_type, o.product_nums as productNums, o.hb_order_status as hbOrderStatus,u.real_name,
+        tsd.shop_name as shopName,d.channel_name
+        from tz_order_refund refund
+        join tz_shop_detail tsd on refund.shop_id = tsd.shop_id
+        join tz_order o on refund.order_id = o.order_id
+        join tz_channel d on o.channel_id = d.id
+        join tz_user u on refund.user_id = u.user_id
+        <where>
+            <if test="orderRefund.orderNumber != null">
+                and refund.order_number = #{orderRefund.orderNumber}
+            </if>
+            <if test="orderRefund.channelIdList != null and orderRefund.channelIdList != ''">
+                and o.channel_id  in
+                <foreach collection="orderRefund.channelIdList" item="channelId" open="(" close=")" separator=",">
+                    #{channelId}
+                </foreach>
+            </if>
+            <if test="orderRefund.refundSn != null">
+                and refund.refund_sn = #{orderRefund.refundSn}
+            </if>
+            <if test="orderRefund.buyerMobile != null">
+                and refund.buyer_mobile = #{orderRefund.buyerMobile}
+            </if>
+            <if test="orderRefund.applyType != null">
+                and refund.apply_type = #{orderRefund.applyType}
+            </if>
+            <if test="orderRefund.returnMoneySts != null and orderRefund.returnMoneySts != 0">
+                and refund.return_money_sts = #{orderRefund.returnMoneySts}
+            </if>
+            <if test="orderRefund.startTime != null">
+                AND refund.create_time &gt;= #{orderRefund.startTime}
+            </if>
+            <if test="orderRefund.endTime != null">
+                AND refund.create_time &lt;= #{orderRefund.endTime}
+            </if>
+        </where>
+        order by refund.apply_time desc
+    </select>
 
 </mapper>

+ 42 - 0
yami-shop-sys/src/main/java/com/yami/shop/sys/dao/SysUserChannelMapper.java

@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018-2999 广州亚米信息科技有限公司 All rights reserved.
+ *
+ * https://www.gz-yami.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.sys.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yami.shop.sys.model.SysUserChannel;
+import com.yami.shop.sys.model.SysUserRole;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 用户与角色对应关系
+ */
+public interface SysUserChannelMapper extends BaseMapper<SysUserChannel> {
+	
+	/**
+	 * 根据角色ID数组,批量删除
+	 */
+	int deleteBatch(@Param("roleIds") Long[] roleIds);
+
+	/**
+	 * 根据用户id删除用户与角色关系
+	 * @param userId
+	 */
+	void deleteByUserId(Long userId);
+
+	/**
+	 * 根据用户id 批量添加用户角色关系
+	 * @param userId
+	 * @param channelIdList
+	 */
+	void insertUserAndUserChannel(@Param("userId") Long userId, @Param("channelIdList") List<Long> channelIdList);
+}

+ 3 - 0
yami-shop-sys/src/main/java/com/yami/shop/sys/dao/SysUserMapper.java

@@ -11,7 +11,9 @@
 package com.yami.shop.sys.dao;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yami.shop.bean.model.Channel;
 import com.yami.shop.bean.model.SysUser;
+import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
 
@@ -33,4 +35,5 @@ public interface SysUserMapper extends BaseMapper<SysUser> {
 	 */
 	SysUser selectByUsername(String username);
 
+    List<Channel> findByUserId(@Param("userId") Long userId);
 }

+ 39 - 0
yami-shop-sys/src/main/java/com/yami/shop/sys/model/SysUserChannel.java

@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018-2999 广州亚米信息科技有限公司 All rights reserved.
+ *
+ * https://www.gz-yami.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.sys.model;
+
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 用户与企业对应关系
+ */
+@Data
+@TableName("tz_sys_user_channel")
+public class SysUserChannel implements Serializable {
+	private static final long serialVersionUID = 1L;
+	@TableId
+	private Long id;
+
+	/**
+	 * 用户ID
+	 */
+	private Long userId;
+
+	/**
+	 * 角色ID
+	 */
+	private Long channelId;
+}

+ 5 - 0
yami-shop-sys/src/main/java/com/yami/shop/sys/service/SysUserService.java

@@ -12,9 +12,12 @@ package com.yami.shop.sys.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.yami.shop.bean.model.Channel;
 import com.yami.shop.common.util.PageParam;
 import com.yami.shop.bean.model.SysUser;
 
+import java.util.List;
+
 
 /**
  * 系统用户
@@ -57,4 +60,6 @@ public interface SysUserService extends IService<SysUser> {
 	SysUser getSysUserById(Long userId);
 
 	IPage<SysUser> selectPage(String username,PageParam<SysUser> page);
+
+    List<Channel> findByUserId(Long userId);
 }

+ 21 - 4
yami-shop-sys/src/main/java/com/yami/shop/sys/service/impl/SysUserServiceImpl.java

@@ -16,10 +16,12 @@ import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.yami.shop.bean.model.Channel;
 import com.yami.shop.bean.model.DeptUser;
 import com.yami.shop.common.util.PageParam;
 import com.yami.shop.service.DeptService;
 import com.yami.shop.service.DeptUserService;
+import com.yami.shop.sys.dao.SysUserChannelMapper;
 import com.yami.shop.sys.dao.SysUserMapper;
 import com.yami.shop.sys.dao.SysUserRoleMapper;
 import com.yami.shop.bean.model.SysUser;
@@ -30,6 +32,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.Date;
+import java.util.List;
 
 
 /**
@@ -42,6 +45,8 @@ import java.util.Date;
 public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
 
     private SysUserRoleMapper sysUserRoleMapper;
+    @Autowired
+    private SysUserChannelMapper sysUserChannelMapper;
 
     private SysUserMapper sysUserMapper;
     @Autowired
@@ -59,6 +64,8 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
         //保存用户与角色关系
         sysUserRoleMapper.insertUserAndUserRole(user.getUserId(), user.getRoleIdList());
 
+        //保存用户与企业关系
+        sysUserChannelMapper.insertUserAndUserChannel(user.getUserId(), user.getChannelIdList());
         saveUserDepartments(user);
     }
 
@@ -87,12 +94,17 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
         //先删除用户与角色关系
         sysUserRoleMapper.deleteByUserId(user.getUserId());
 
-        if (CollUtil.isEmpty(user.getRoleIdList())) {
+        //删除用户与企业关系表
+        sysUserChannelMapper.deleteByUserId(user.getUserId());
+        if (CollUtil.isEmpty(user.getRoleIdList())&& CollUtil.isEmpty(user.getChannelIdList())) {
             return;
+        }else if (!CollUtil.isEmpty(user.getRoleIdList())){
+            //保存用户与角色关系
+            sysUserRoleMapper.insertUserAndUserRole(user.getUserId(), user.getRoleIdList());
+        }else if (!CollUtil.isEmpty(user.getChannelIdList())){
+            //保存用户与企业关系
+            sysUserChannelMapper.insertUserAndUserChannel(user.getUserId(), user.getRoleIdList());
         }
-        //保存用户与角色关系
-        sysUserRoleMapper.insertUserAndUserRole(user.getUserId(), user.getRoleIdList());
-
 
         // 更新用户部门信息
         updateUserDepartments(user);
@@ -160,4 +172,9 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
         return sysUserPage;
 
     }
+
+    @Override
+    public List<Channel> findByUserId(Long userId) {
+        return sysUserMapper.findByUserId(userId);
+    }
 }

+ 23 - 0
yami-shop-sys/src/main/resources/mapper/SysUserChannelMapper.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="com.yami.shop.sys.dao.SysUserChannelMapper">
+
+	<delete id="deleteBatch">
+		delete from tz_sys_user_role where role_id in
+		<foreach item="roleId" collection="roleIds" open="(" separator="," close=")">
+			#{roleId}
+		</foreach>
+	</delete>
+	
+	<delete id="deleteByUserId">
+		delete from tz_sys_user_channel where user_id = #{userId}
+	</delete>
+	
+	<insert id="insertUserAndUserChannel">
+	  	insert into tz_sys_user_channel (user_id,channel_id) values
+	  	<foreach collection="channelIdList" item="channelId" separator=",">
+	  		(#{userId},#{channelId})
+	  	</foreach>
+  </insert>
+</mapper>

+ 5 - 0
yami-shop-sys/src/main/resources/mapper/SysUserMapper.xml

@@ -14,4 +14,9 @@
 	<select id="selectByUsername" resultType="com.yami.shop.bean.model.SysUser">
 		select * from tz_sys_user where username = #{username}
 	</select>
+    <select id="findByUserId" resultType="com.yami.shop.bean.model.Channel">
+		select c.* from tz_sys_user a
+		left join tz_sys_user_channel b on a.user_id = b.user_id
+		left join  tz_channel c on  b.channel_id = c.id
+	</select>
 </mapper>