Просмотр исходного кода

顺丰批量导入 新增批次号

zxf недель назад: 2
Родитель
Сommit
2f0deeb56d
12 измененных файлов с 856 добавлено и 14 удалено
  1. 3 0
      jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/JdLogisticsSystemApplication.java
  2. 6 0
      jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/constant/JDDictConstants.java
  3. 59 2
      jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/controller/BizWaybillOrderController.java
  4. 10 0
      jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/domain/BizWaybillOrder.java
  5. 72 0
      jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/domain/dto/SfOrderImportDTO.java
  6. 351 0
      jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/handler/SFOrderDynamicTemplateHandler.java
  7. 6 0
      jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/service/IBizWaybillOrderService.java
  8. 315 10
      jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/service/impl/BizWaybillOrderServiceImpl.java
  9. 3 1
      jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/service/impl/SFLogisticsService.java
  10. 4 0
      jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java
  11. 4 0
      jd-logistics-modules/jd-logistics-system/src/main/resources/mapper/logistics/BizWaybillOrderMapper.xml
  12. 23 1
      jd-logistics-modules/jd-logistics-system/src/main/resources/mapper/system/SysDictDataMapper.xml

+ 3 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/JdLogisticsSystemApplication.java

@@ -4,11 +4,14 @@ import com.ruoyi.common.security.annotation.EnableCustomConfig;
 import com.ruoyi.common.security.annotation.EnableRyFeignClients;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableAsync;
+
 /**
  * 系统模块
  *
  * @author lydgt
  */
+@EnableAsync
 @EnableCustomConfig
 @EnableRyFeignClients
 @SpringBootApplication

+ 6 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/constant/JDDictConstants.java

@@ -6,4 +6,10 @@ public class JDDictConstants {
     public static final String PRODUCT_CODE_ED_M_0001 = "ed-m-0001";
     public static final String PRODUCT_CODE_ED_M_0002 = "ed-m-0002";
     public static final String PRODUCT_CODE_FR_M_0004 = "fr-m-0004";
+
+    //顺丰
+
+
+    public static final String PRODUCT_CODE_SF_01 = "1";
+    public static final String PRODUCT_CODE_SF_02 = "2";
 }

+ 59 - 2
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/controller/BizWaybillOrderController.java

@@ -1,6 +1,8 @@
 package com.ruoyi.logistics.controller;
 
+import com.alibaba.excel.EasyExcel;
 import com.ruoyi.common.core.exception.ServiceException;
+import com.ruoyi.common.core.utils.StringUtils;
 import com.ruoyi.common.core.utils.poi.ExcelUtil;
 import com.ruoyi.common.core.web.controller.BaseController;
 import com.ruoyi.common.core.web.domain.AjaxResult;
@@ -14,6 +16,8 @@ import com.ruoyi.logistics.domain.BizWaybillOrder;
 import com.ruoyi.logistics.domain.SysDeptRate;
 import com.ruoyi.logistics.domain.dto.JDOrderDTO;
 import com.ruoyi.logistics.handler.JDOrderDynamicTemplateHandler;
+import com.ruoyi.logistics.domain.dto.SfOrderImportDTO;
+import com.ruoyi.logistics.handler.SFOrderDynamicTemplateHandler;
 import com.ruoyi.logistics.service.IBizWaybillOrderService;
 import com.ruoyi.logistics.service.impl.SysDeptRateServiceImpl;
 import com.ruoyi.system.api.domain.SysDictData;
@@ -26,6 +30,7 @@ import org.springframework.web.multipart.MultipartFile;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Map;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -168,7 +173,7 @@ public class BizWaybillOrderController extends BaseController
     /**
      * 批量下单
      */
-    @RequiresPermissions("system:order:add")
+  //  @RequiresPermissions("system:order:add")
     @Log(title = "批量下单", businessType = BusinessType.INSERT)
     @PostMapping("/batchAdd")
     public AjaxResult batchAdd(@RequestParam("file") MultipartFile file,
@@ -197,8 +202,34 @@ public class BizWaybillOrderController extends BaseController
                 // 调用批量下单方法
                 bizWaybillOrderService.batchInsertJDBizWaybillOrder(orderList, orderType);
             }
+            if (2 == orderType) {
+                ExcelUtil<SfOrderImportDTO> util = new ExcelUtil<>(SfOrderImportDTO.class);
+               // List<SfOrderImportDTO> orderList = util.importExcel(file.getInputStream());
+
+                List<SfOrderImportDTO> orderList = EasyExcel.read(file.getInputStream())
+                        .head(SfOrderImportDTO.class)
+                        .sheet()
+                        .doReadSync();
+
+                // 校验数据
+                if (orderList == null || orderList.isEmpty()) {
+                    return AjaxResult.error("Excel 文件中没有有效的订单数据");
+                }
+                // 2. 统一校验(返回错误字符串)
+                Map<String,Object> result = bizWaybillOrderService.validateAndGetError(orderList);
+
+                // 3. 有错误 → 直接返回字符串
+                if (StringUtils.isNotBlank(result.get("errorMsg").toString())) {
+                    return AjaxResult.error(result.get("errorMsg").toString());
+                }
+
+                // 调用异步批量下单方法
+               bizWaybillOrderService.asyncBatchInsert((List<BizWaybillOrder>) result.get("orederList"), orderType);
+            }
+
+            return AjaxResult.success("校验通过,已开始异步导入,清稍后刷新订单进行查看仔细核对!");
+           // return AjaxResult.success(String.format("批量下单完成!成功 %d 单,失败 %d 单", result[0], result[1]));
 
-            return AjaxResult.success("批量下单完成!");
 
         } catch (Exception e) {
             log.error("批量下单异常", e);
@@ -206,6 +237,8 @@ public class BizWaybillOrderController extends BaseController
         }
     }
 
+
+
     /**
      * 下载批量下单模板
      */
@@ -218,6 +251,30 @@ public class BizWaybillOrderController extends BaseController
             JDOrderDynamicTemplateHandler handler = new JDOrderDynamicTemplateHandler();
             handler.exportDynamicTemplate(response, productCodes, goodsNames);
         }
+        if (2 == orderType) {
+            List<String> productCodes = getProductCodeListSf(); // 从数据库获取产品类型
+            List<String> goodsNames = getGoodsNameList(); // 物品名称
+            SFOrderDynamicTemplateHandler handler = new SFOrderDynamicTemplateHandler();
+            handler.exportDynamicTemplate(response, productCodes, goodsNames);
+        }
+    }
+
+    private List<String> getProductCodeListSf() {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+
+        SysDeptRate sysDeptRate = new SysDeptRate();
+        sysDeptRate.setDeptId(loginUser.getSysUser().getDeptId());
+        sysDeptRate.setCompanyType("2");
+        List<SysDeptRate> sysDeptRates = sysDeptRateService.selectSysDeptRateList(sysDeptRate);
+        List<String> productNames = new ArrayList<>();
+        for (SysDeptRate item : sysDeptRates) {
+            if (JDDictConstants.PRODUCT_CODE_SF_01.equals(item.getProductType())) {
+                productNames.add("顺丰特快");
+            } else if (JDDictConstants.PRODUCT_CODE_SF_02.equals(item.getProductType())) {
+                productNames.add("顺丰标快");
+            }
+        }
+        return productNames;
     }
 
     /**

+ 10 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/domain/BizWaybillOrder.java

@@ -176,6 +176,8 @@ public class BizWaybillOrder extends BaseEntity
 
     /** 面单pdf */
     private String pdfUrl;
+    /** 批次号 */
+    private String batchNum;
 
     /** 运单费用 */
     @Excel(name = "运单费用")
@@ -567,6 +569,14 @@ public class BizWaybillOrder extends BaseEntity
         this.totalAmount = totalAmount;
     }
 
+    public void setBatchNum(String batchNum) {
+        this.batchNum = batchNum;
+    }
+
+    public String getBatchNum() {
+        return batchNum;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

+ 72 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/domain/dto/SfOrderImportDTO.java

@@ -0,0 +1,72 @@
+package com.ruoyi.logistics.domain.dto;
+
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.alibaba.excel.annotation.write.style.ColumnWidth;
+import com.alibaba.excel.annotation.write.style.HeadRowHeight;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+@HeadRowHeight(20)
+@ExcelIgnoreUnannotated
+@Data
+public class SfOrderImportDTO implements Serializable {
+    private static final long serialVersionUID = 1L;
+    private String waybillNo;
+    @ExcelProperty(value = "寄件人姓名", order = 0)
+    @ColumnWidth(15)
+    private String senderName;
+    @ExcelProperty(value = "寄件人电话", order = 1)
+    @ColumnWidth(15)
+    private String senderPhone;
+    /** 发件省 */
+    private String senderProvince;
+    /** 发件市 */
+    private String senderCity;
+    /** 发件区县 */
+    private String senderCounty;
+    @ExcelProperty(value = "寄件详细地址", order = 2)
+    @ColumnWidth(30)
+    private String senderAddress;
+    @ExcelProperty(value = "收件人姓名", order = 3)
+    @ColumnWidth(15)
+    private String receiverName;
+    @ExcelProperty(value = "收件人电话", order = 4)
+    @ColumnWidth(15)
+    private String receiverPhone;
+    /** 收件省 */
+    private String receiverProvince;
+    /** 收件市 */
+    private String receiverCity;
+    /** 收件区县 */
+    private String receiverCounty;
+    @ExcelProperty(value = "收件详细地址", order = 5)
+    @ColumnWidth(30)
+    private String receiverAddress;
+    @ExcelProperty(value = "物品名称", order = 6)
+    @ColumnWidth(15)
+    private String goodsName;
+    @ExcelProperty(value = "物品重量", order = 7)
+    @ColumnWidth(15)
+    private BigDecimal goodsWeight;
+    @ExcelProperty(value = "物品体积", order = 8)
+    @ColumnWidth(15)
+    private BigDecimal goodsVolume;
+    @ExcelProperty(value = "物品数量", order = 9)
+    @ColumnWidth(15)
+    private Long goodsQty;
+    /** 上门取件开始时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date sendStartTime;
+    @ExcelProperty(value = "产品类型", order = 10)
+    @ColumnWidth(15)
+    private String productCode;
+    @ExcelProperty(value = "保价金额", order = 11)
+    @ColumnWidth(15)
+    private String protecPrice;
+
+}

+ 351 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/handler/SFOrderDynamicTemplateHandler.java

@@ -0,0 +1,351 @@
+package com.ruoyi.logistics.handler;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.write.handler.SheetWriteHandler;
+import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
+import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
+import com.ruoyi.logistics.domain.dto.SfOrderImportDTO;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddressList;
+import org.apache.poi.xssf.usermodel.XSSFDataValidation;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SFOrderDynamicTemplateHandler {
+
+    /**
+     * 导出带动态下拉框的 Excel 模板
+     *
+     * @param response HTTP 响应对象
+     * @param productCodes 产品类型下拉列表数据
+     * @param goodsNames 物品名称下拉列表数据
+     * @throws IOException IO 异常
+     */
+    public void exportDynamicTemplate(HttpServletResponse response,
+            List<String> productCodes,
+            List<String> goodsNames) throws IOException {
+
+        // 设置响应头
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setCharacterEncoding("utf-8");
+        String fileName = URLEncoder.encode("顺丰批量下单模板", "UTF-8").replaceAll("\\+", "%20");
+        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
+
+        // 创建 Excel 写入器,注册自定义 SheetWriteHandler 和 RowWriteHandler
+        EasyExcel.write(response.getOutputStream(), SfOrderImportDTO.class)
+                .sheet("订单")
+                .registerWriteHandler(createDropdownSheetHandler(productCodes, goodsNames))
+                .registerWriteHandler(createTextFormatRowHandler())
+                .doWrite(ArrayList::new);
+    }
+
+    /**
+     * 创建下拉框数据验证处理器
+     *
+     * @param productCodes 产品类型下拉列表数据
+     * @param goodsNames 物品名称下拉列表数据
+     * @return SheetWriteHandler 处理器
+     */
+    private SheetWriteHandler createDropdownSheetHandler(List<String> productCodes,
+            List<String> goodsNames) {
+        return new SheetWriteHandler() {
+            @Override
+            public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder,
+                                         WriteSheetHolder writeSheetHolder) {
+                Sheet sheet = writeSheetHolder.getSheet();
+                Workbook workbook = writeWorkbookHolder.getWorkbook();
+
+                // 1. 为"物品名称"列(第 G 列,索引 6)添加下拉框
+                if (goodsNames != null && !goodsNames.isEmpty()) {
+                    addDropdownValidation(workbook, sheet, goodsNames, 6, "GoodsNames");
+                }
+
+                // 2. 为"产品类型"列(第 M 列,索引 12)添加下拉框
+                if (productCodes != null && !productCodes.isEmpty()) {
+                    addDropdownValidation(workbook, sheet, productCodes, 10, "ProductCodes");
+                }
+
+
+
+                // 5. 添加数值列的数据验证(不能为负数)
+                // 物品重量(第 H 列,索引 7)
+                addNumberValidation(workbook, sheet, 7, "物品重量");
+                // 物品体积(第 I 列,索引 8)
+                addNumberValidation(workbook, sheet, 8, "物品体积");
+                // 物品数量(第 J 列,索引 9)
+                addNumberValidation(workbook, sheet, 9, "物品数量");
+                // 保价金额(第 O 列,索引 14)
+                addNumberValidation(workbook, sheet, 11, "保价金额");
+
+           /*     // 6. 添加时间列的数据验证(格式:yyyy-MM-dd HH:00:00)
+                // 上门取件开始时间(第 K 列,索引 10)
+                addDateTimeValidation(workbook, sheet, 10, "上门取件开始时间");
+                // 上门取件结束时间(第 L 列,索引 11)
+                addDateTimeValidation(workbook, sheet, 11, "上门取件结束时间");*/
+            }
+        };
+    }
+
+    /**
+     * 添加下拉框数据验证
+     *
+     * @param workbook 工作簿对象
+     * @param sheet 工作表对象
+     * @param dataList 下拉选项数据列表
+     * @param columnIndex 列索引(从 0 开始)
+     * @param rangeName 命名区域名称
+     */
+    private void addDropdownValidation(Workbook workbook, Sheet sheet,
+            List<String> dataList,
+    int columnIndex,
+    String rangeName) {
+        if (dataList == null || dataList.isEmpty()) {
+            return;
+        }
+
+        // 检查是否已存在该命名区域
+        Name existingName = workbook.getName(rangeName);
+        if (existingName == null) {
+            // 1. 创建隐藏的工作表存储下拉选项数据
+            String hiddenSheetName = "_hidden_" + rangeName;
+            Sheet hiddenSheet = workbook.createSheet(hiddenSheetName);
+
+            // 填充数据到隐藏工作表的第 1 列
+            for (int i = 0; i < dataList.size(); i++) {
+                Row row = hiddenSheet.createRow(i);
+                Cell cell = row.createCell(0);
+                cell.setCellValue(dataList.get(i));
+            }
+
+            // 隐藏该工作表
+            workbook.setSheetHidden(workbook.getSheetIndex(hiddenSheet), true);
+
+            // 2. 创建命名区域
+            Name namedCell = workbook.createName();
+            namedCell.setNameName(rangeName);
+            String formula = hiddenSheet.getSheetName() + "!$A$1:$A$" + dataList.size();
+            namedCell.setRefersToFormula(formula);
+        }
+
+        // 3. 设置数据验证规则
+        DataValidationHelper helper = sheet.getDataValidationHelper();
+        DataValidationConstraint constraint = helper.createFormulaListConstraint(rangeName);
+
+        // 设置下拉框应用范围(从第 2 行开始,第 1 行是标题)
+        CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, columnIndex, columnIndex);
+
+        DataValidation validation = helper.createValidation(constraint, addressList);
+        configureValidation(validation);
+
+        sheet.addValidationData(validation);
+    }
+
+    /**
+     * 为已有命名区域的列添加下拉框数据验证
+     *
+     * @param workbook 工作簿对象
+     * @param sheet 工作表对象
+     * @param columnIndex 列索引(从 0 开始)
+     * @param rangeName 已存在的命名区域名称
+     */
+    private void addDropdownValidationToExistingRange(Workbook workbook, Sheet sheet,
+    int columnIndex,
+    String rangeName) {
+        // 验证命名区域是否存在
+        Name existingName = workbook.getName(rangeName);
+        if (existingName == null) {
+            return; // 如果命名区域不存在,直接返回
+        }
+
+        // 设置数据验证规则
+        DataValidationHelper helper = sheet.getDataValidationHelper();
+        DataValidationConstraint constraint = helper.createFormulaListConstraint(rangeName);
+
+        // 设置下拉框应用范围(从第 2 行开始,第 1 行是标题)
+        CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, columnIndex, columnIndex);
+
+        DataValidation validation = helper.createValidation(constraint, addressList);
+        configureValidation(validation);
+
+        sheet.addValidationData(validation);
+    }
+
+    /**
+     * 配置数据验证的通用属性
+     *
+     * @param validation 数据验证对象
+     */
+    private void configureValidation(DataValidation validation) {
+        // 允许空白输入
+        validation.setSuppressDropDownArrow(true);
+        validation.setShowErrorBox(true);
+        validation.setShowPromptBox(true);
+
+        // 处理 POI 的兼容性问题
+        if (validation instanceof XSSFDataValidation) {
+            ((XSSFDataValidation) validation).setSuppressDropDownArrow(true);
+            ((XSSFDataValidation) validation).setShowErrorBox(true);
+        }
+    }
+
+
+    /**
+     * 获取是/否选项下拉列表
+     */
+    private List<String> getYesNoOptions() {
+        return java.util.Arrays.asList("是", "否");
+    }
+
+    /**
+     * 添加数值数据验证(必须大于 0)
+     *
+     * @param workbook 工作簿对象
+     * @param sheet 工作表对象
+     * @param columnIndex 列索引(从 0 开始)
+     * @param fieldName 字段名称(用于错误提示)
+     */
+    private void addNumberValidation(Workbook workbook, Sheet sheet,
+    int columnIndex, String fieldName) {
+        DataValidationHelper helper = sheet.getDataValidationHelper();
+
+        // 使用自定义公式验证:数值必须大于 0
+        String excelCol = columnToLetter(columnIndex);
+        String formula = excelCol + "2>0";
+
+        DataValidationConstraint constraint = helper.createCustomConstraint(formula);
+
+        // 设置验证范围(从第 2 行开始,第 1 行是标题)
+        CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, columnIndex, columnIndex);
+
+        DataValidation validation = helper.createValidation(constraint, addressList);
+
+        // 设置错误提示
+        validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
+        validation.createErrorBox("输入值无效", fieldName + "必须大于 0,请输入正数!");
+        validation.setShowErrorBox(true);
+
+        // 允许空白输入
+        validation.setSuppressDropDownArrow(true);
+
+        if (validation instanceof XSSFDataValidation) {
+            ((XSSFDataValidation) validation).setSuppressDropDownArrow(true);
+            ((XSSFDataValidation) validation).setShowErrorBox(true);
+        }
+
+        sheet.addValidationData(validation);
+    }
+
+    /**
+     * 添加日期时间数据验证(格式:yyyy-MM-dd HH:00:00)
+     *
+     * @param workbook 工作簿对象
+     * @param sheet 工作表对象
+     * @param columnIndex 列索引(从 0 开始)
+     * @param fieldName 字段名称(用于错误提示)
+     */
+    private void addDateTimeValidation(Workbook workbook, Sheet sheet,
+    int columnIndex, String fieldName) {
+        DataValidationHelper helper = sheet.getDataValidationHelper();
+
+        // 将列索引转换为 Excel 列字母(如 K、L)
+        String excelCol = columnToLetter(columnIndex);
+
+        // 构建验证公式:严格按照 yyyy-MM-dd HH:00:00 格式
+        // 验证要点:
+        // 1. 总长度必须为 19 个字符
+        // 2. 固定位置的字符必须是:- - 空格 : :
+        // 3. 分钟 (15-16 位) 和秒 (18-19 位) 必须是 00
+        // 4. 年、月、日、时必须是数字
+        String formula = "AND(" +
+                "LEN(" + excelCol + "2)=19," +                                    // 长度=19
+                "MID(" + excelCol + "2,5,1)=\"-\"," +                             // 第 5 位=-
+                "MID(" + excelCol + "2,8,1)=\"-\"," +                             // 第 8 位=-
+                "MID(" + excelCol + "2,11,1)=\" \"," +                            // 第 11 位=空格
+                "MID(" + excelCol + "2,14,1)=\":\"," +                            // 第 14 位=:
+                "MID(" + excelCol + "2,17,1)=\":\"," +                            // 第 17 位=:
+                "MID(" + excelCol + "2,15,2)=\"00\"," +                           // 分钟=00
+                "MID(" + excelCol + "2,18,2)=\"00\"," +                           // 秒=00
+                "--MID(" + excelCol + "2,1,4)>=1900," +                           // 年>=1900
+                "--MID(" + excelCol + "2,1,4)<=9999," +                           // 年<=9999
+                "--MID(" + excelCol + "2,6,2)>=1," +                              // 月>=1
+                "--MID(" + excelCol + "2,6,2)<=12," +                             // 月<=12
+                "--MID(" + excelCol + "2,9,2)>=1," +                              // 日>=1
+                "--MID(" + excelCol + "2,9,2)<=31," +                             // 日<=31
+                "--MID(" + excelCol + "2,12,2)>=0," +                             // 时>=0
+                "--MID(" + excelCol + "2,12,2)<=23" +                             // 时<=23
+                ")";
+
+        DataValidationConstraint constraint = helper.createCustomConstraint(formula);
+
+        // 设置验证范围(从第 2 行开始,第 1 行是标题)
+        CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, columnIndex, columnIndex);
+
+        DataValidation validation = helper.createValidation(constraint, addressList);
+
+        // 设置输入提示,引导用户输入格式
+        validation.createPromptBox(fieldName + "输入提示", "请输入日期和时间,格式:yyyy-MM-dd HH:00:00(例如:2026-01-01 10:00:00)");
+        validation.setShowPromptBox(true);
+
+        // 设置错误提示
+        validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
+        validation.createErrorBox("输入值无效", fieldName + "格式不正确,请严格按照 yyyy-MM-dd HH:00:00 格式输入(分钟和秒固定为 00)!");
+        validation.setShowErrorBox(true);
+
+        // 允许空白输入
+        validation.setSuppressDropDownArrow(true);
+
+        if (validation instanceof XSSFDataValidation) {
+            ((XSSFDataValidation) validation).setSuppressDropDownArrow(true);
+            ((XSSFDataValidation) validation).setShowErrorBox(true);
+        }
+
+        sheet.addValidationData(validation);
+    }
+
+    /**
+     * 将列索引转换为 Excel 列字母
+     *
+     * @param columnIndex 列索引(从 0 开始)
+     * @return Excel 列字母
+     */
+    private String columnToLetter(int columnIndex) {
+        StringBuilder result = new StringBuilder();
+        int index = columnIndex + 1; // 转换为从 1 开始
+        while (index > 0) {
+            index--;
+            result.insert(0, (char) ('A' + index % 26));
+            index /= 26;
+        }
+        return result.toString();
+    }
+
+    /**
+     * 创建文本格式的 RowWriteHandler
+     * 用于设置上门取件开始时间和结束时间列为文本格式
+     *
+     * @return RowWriteHandler 处理器
+     */
+    private SheetWriteHandler createTextFormatRowHandler() {
+        return new SheetWriteHandler() {
+            @Override
+            public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
+                Sheet sheet = writeSheetHolder.getSheet();
+                DataFormat dataFormat = writeWorkbookHolder.getWorkbook().createDataFormat();
+
+                // 创建文本格式单元格样式
+                CellStyle textCellStyle = writeWorkbookHolder.getWorkbook().createCellStyle();
+                textCellStyle.setDataFormat(dataFormat.getFormat("@")); // 等同于setDataFormat(TEXT_FORMAT_CODE)
+
+                // 为sheet的所有列设置默认文本格式(这里设置前100列,可根据需求调整范围)
+                int maxColumnNum = 100;
+                for (int i = 0; i < maxColumnNum; i++) {
+                    sheet.setDefaultColumnStyle(i, textCellStyle);
+                }
+            }
+        };
+    }
+}

+ 6 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/service/IBizWaybillOrderService.java

@@ -10,8 +10,10 @@ import com.ruoyi.logistics.domain.BizWaybillOrder;
 import com.ruoyi.logistics.domain.dto.OrderStatisticsDto;
 import com.ruoyi.logistics.domain.dto.DailyOrderStatisticsDto;
 import com.ruoyi.logistics.domain.dto.JDOrderDTO;
+import com.ruoyi.logistics.domain.dto.SfOrderImportDTO;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 运单管理Service接口
@@ -106,4 +108,8 @@ public interface IBizWaybillOrderService
      * @return 结果数组,每个元素表示对应订单的结果 [0:成功数,1:失败数]
      */
     public int batchInsertJDBizWaybillOrder(List<JDOrderDTO> orderList, Integer orderType);
+
+    void asyncBatchInsert(List<BizWaybillOrder> orderList, Integer orderType);
+
+    Map<String,Object> validateAndGetError(List<SfOrderImportDTO> orderList);
 }

+ 315 - 10
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/service/impl/BizWaybillOrderServiceImpl.java

@@ -3,6 +3,8 @@ package com.ruoyi.logistics.service.impl;
 import java.io.UnsupportedEncodingException;
 import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
@@ -23,6 +25,7 @@ import com.ruoyi.logistics.constant.JDDictConstants;
 import com.ruoyi.logistics.constant.RedisCacheConstants;
 import com.ruoyi.logistics.domain.BizAddressBook;
 import com.ruoyi.logistics.domain.BizWaybillCostDetails;
+import com.ruoyi.logistics.domain.dto.*;
 import com.ruoyi.logistics.domain.SysDeptRate;
 import com.ruoyi.logistics.domain.dto.OrderStatisticsDto;
 import com.ruoyi.logistics.domain.dto.RecentAddressDto;
@@ -31,16 +34,18 @@ import com.ruoyi.logistics.enums.OrderStatusEnum;
 import com.ruoyi.logistics.mapper.BizAddressBookMapper;
 import com.ruoyi.logistics.mapper.BizWaybillCostDetailsMapper;
 import com.ruoyi.logistics.service.LogisticsOrderService;
+import com.ruoyi.system.api.domain.SysDictData;
 import com.ruoyi.system.api.model.LoginUser;
+import com.ruoyi.system.mapper.SysDictDataMapper;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.time.DateFormatUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import com.ruoyi.common.redis.service.RedisService;
 import com.ruoyi.logistics.mapper.BizWaybillOrderMapper;
 import com.ruoyi.logistics.domain.BizWaybillOrder;
-import com.ruoyi.logistics.domain.dto.DailyOrderStatisticsDto;
 import com.ruoyi.logistics.service.IBizWaybillOrderService;
 
 /**
@@ -72,10 +77,10 @@ public class BizWaybillOrderServiceImpl implements IBizWaybillOrderService
 
     @Autowired
     private SysDeptRateServiceImpl sysDeptRateService;
-
     @Autowired
-    private Kuaidi100Service kuaidi100Service;
-
+    Kuaidi100Service kuaidi100Service;
+    @Autowired
+    private SysDictDataMapper dictDataMapper;
     /**
      * 查询运单管理
      * 
@@ -142,10 +147,8 @@ public class BizWaybillOrderServiceImpl implements IBizWaybillOrderService
         bizWaybillOrder.setCreateTime(DateUtils.getNowDate());
         bizWaybillOrder.setUserId(loginUser.getUserid());
         bizWaybillOrder.setDeptId(loginUser.getSysUser().getDeptId());
-        
         // 保存寄件地址和收件地址到Redis作为用户最近使用地址
         saveRecentAddresses(bizWaybillOrder, loginUser.getUserid());
-        
         return bizWaybillOrderMapper.insertBizWaybillOrder(bizWaybillOrder);
     }
 
@@ -450,7 +453,7 @@ public class BizWaybillOrderServiceImpl implements IBizWaybillOrderService
         if (dto == null) {
             throw new ServiceException("数据异常!订单数据不能为空");
         }
-        
+
         // 校验寄件人信息
         if (StringUtils.isBlank(dto.getSenderName())) {
             throw new ServiceException("数据异常!寄件人姓名不能为空");
@@ -512,7 +515,7 @@ public class BizWaybillOrderServiceImpl implements IBizWaybillOrderService
         } catch (Exception e) {
             throw new ServiceException("数据异常!收件详细地址识别有误");
         }
-        
+
         // 校验货物信息
         if (StringUtils.isBlank(dto.getGoodsName())) {
             throw new ServiceException("数据异常!物品名称不能为空");
@@ -526,7 +529,7 @@ public class BizWaybillOrderServiceImpl implements IBizWaybillOrderService
         if (dto.getGoodsQty() == null) {
             throw new ServiceException("数据异常!物品数量不能为空");
         }
-        
+
         // 校验取件时间
         if (dto.getSendStartTime() == null || dto.getSendEndTime() == null) {
             throw new ServiceException("数据异常!上门取件开始时间和结束时间不能为空");
@@ -536,7 +539,7 @@ public class BizWaybillOrderServiceImpl implements IBizWaybillOrderService
         if (dto.getPickupStartTime().getTime() > dto.getPickupEndTime().getTime()) {
             throw new ServiceException("数据异常!上门取件开始时间不能大于上门取件结束时间");
         }
-        
+
         // 校验产品类型
         if (StringUtils.isBlank(dto.getProductCode())) {
             throw new ServiceException("数据异常!产品类型不能为空");
@@ -544,6 +547,308 @@ public class BizWaybillOrderServiceImpl implements IBizWaybillOrderService
         if (!collect.contains(dto.getProductCode())) {
             throw new ServiceException("数据异常!产品类型不存在");
         }
+
+
+    }
+    @Async
+    @Override
+    public void asyncBatchInsert(List<BizWaybillOrder> orderList, Integer orderType) {
+        String batchNum = redisIdGenerator.generateUniqueId("SFPC");
+
+        int successCount = 0;
+        int failCount = 0;
+        List<String> errorMessages = new ArrayList<>();
+        List<BizWaybillOrder> failList= new ArrayList<>();
+        for (BizWaybillOrder dto : orderList) {
+            try {
+                //还需要校验一下收件地址寄件地址的省市区信息是否合规
+                // ===================== 解析 发件地址 省市区 =====================
+                Map<String, Object> senderParam = new HashMap<>();
+                senderParam.put("param", dto.getSenderAddress());
+                Map<String, Object> senderResultMap = kuaidi100Service.queryIcrExpres(senderParam);
+                Map<String, Object> senderData = (Map<String, Object>) senderResultMap.get("data");
+                List<Map<String, Object>> senderResultList = (List<Map<String, Object>>) senderData.get("result");
+                Map<String, Object> senderResult = senderResultList.get(0);
+                Map<String, Object> senderXzq = (Map<String, Object>) senderResult.get("xzq");
+                // 从 fullName 拆分 省,市,区
+                String senderFullName = (String) senderXzq.get("fullName");
+                String[] senderSplit = senderFullName.split(",");
+                String senderProvince = senderSplit[0];
+                String senderCity = senderSplit[1];
+                String senderDistrict = senderSplit[2];
+                dto.setSenderProvince(senderProvince);
+                dto.setSenderCity(senderCity);
+                dto.setSenderCounty(senderDistrict);
+                // ===================== 解析 收件地址 省市区 =====================
+                Map<String, Object> receiverParam = new HashMap<>();
+                receiverParam.put("param", dto.getReceiverAddress());
+                Map<String, Object> receiverResultMap = kuaidi100Service.queryIcrExpres(receiverParam);
+                Map<String, Object> receiverData = (Map<String, Object>) receiverResultMap.get("data");
+                List<Map<String, Object>> receiverResultList = (List<Map<String, Object>>) receiverData.get("result");
+                Map<String, Object> receiverResult = receiverResultList.get(0);
+                Map<String, Object> receiverXzq = (Map<String, Object>) receiverResult.get("xzq");
+                // 从 fullName 拆分 省,市,区
+                String receiverFullName = (String) receiverXzq.get("fullName");
+                String[] receiverSplit = receiverFullName.split(",");
+                String receiverProvince = receiverSplit[0];
+                String receiverCity = receiverSplit[1];
+                String receiverDistrict = receiverSplit[2];
+                dto.setReceiverProvince(receiverProvince);
+                dto.setReceiverCity(receiverCity);
+                dto.setReceiverCounty(receiverDistrict);
+
+                // 调用下单接口
+                JSONObject orderObject = logisticsOrderService.createOrder(dto);
+                if (!orderObject.getBooleanValue("success")) {
+                    log.warn("下单失败!发件人:{}, 收件人:{}, 原因:{}",
+                            dto.getSenderName(), dto.getReceiverName(), orderObject.getString("msg"));
+                    failCount++;
+                    errorMessages.add(String.format("订单 %s-%s 下单失败:%s",
+                            dto.getSenderName(), dto.getReceiverName(), orderObject.getString("msg")));
+                    failList.add(dto);
+                    continue;
+                }
+                //顺丰物流返回的运单号
+                JSONArray waybillNoArray=orderObject.getJSONArray("data");
+                for (int i = 0; i < waybillNoArray.size(); i++) {
+                    JSONObject waybillNoJSONObject = waybillNoArray.getJSONObject(i);
+                    int waybillType = waybillNoJSONObject.getIntValue("waybillType");
+                    String  waybillNo = waybillNoJSONObject.getString("waybillNo");
+                    if(waybillType==1){
+                        dto.setExternalWaybillNo(waybillNo) ;
+                    }
+                }
+                // 设置默认值
+                dto.setPickupType(1);
+                dto.setCreateTime(DateUtils.getNowDate());
+                dto.setBatchNum(batchNum);
+
+                // 保存到数据库
+                int result = bizWaybillOrderMapper.insertBizWaybillOrder(dto);
+                if (result > 0) {
+                    successCount++;
+                    // 保存最近使用地址
+                    saveRecentAddresses(dto, dto.getUserId());
+                } else {
+                    failCount++;
+                    errorMessages.add(String.format("订单 %s-%s 保存失败",
+                            dto.getSenderName(), dto.getReceiverName()));
+                }
+            } catch (Exception e) {
+                log.error("批量下单异常!发件人:{}, 收件人:{}",
+                        dto.getSenderName(), dto.getReceiverName(), e);
+                failCount++;
+                errorMessages.add(String.format("订单 %s-%s 异常:%s",
+                        dto.getSenderName(), dto.getReceiverName(), e.getMessage()));
+            }
+        }
+        log.info("批量下单完成!总数:{}, 成功:{}, 失败:{}", orderList.size(), successCount, failCount);
+
+    }
+
+    @Override
+    public Map<String,Object> validateAndGetError(List<SfOrderImportDTO> orderList) {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        Map<String,Object> result= new HashMap<>();
+        List<BizWaybillOrder> bizWaybillOrderList= new ArrayList<>();
+        StringBuilder errorMsg = new StringBuilder();
+        List<String> rowErrors = new ArrayList<>();
+        //先把类型拿出来
+        Map productParam= new HashMap<>();
+        productParam.put("dictType","sf_logistics_product_code");
+        productParam.put("deptId",loginUser.getSysUser().getDeptId());
+        List<SysDictData> allProductDictList = dictDataMapper.selectDictDataSfProductList(productParam);
+       // 转成 Map:label -> value,方便快速查找
+       Map<String, String> productMap = allProductDictList.stream()
+                .collect(Collectors.toMap(
+                        SysDictData::getDictLabel,
+                        SysDictData::getDictValue,
+                        (existing, replacement) -> existing // 重复 label 保留第一个
+                ));
+        for (int i = 0; i < orderList.size(); i++) {
+            SfOrderImportDTO vo = orderList.get(i);
+            int row = i + 2; // 行号从1开始
+            try {
+                if (StringUtils.isBlank(vo.getSenderPhone())) {
+                    rowErrors.add("寄件人手机号不能为空");
+                }
+                if (StringUtils.isBlank(vo.getReceiverPhone())) {
+                    rowErrors.add("收件人手机号不能为空");
+                }
+                if (StringUtils.isBlank(vo.getReceiverPhone()) || !vo.getReceiverPhone().matches("1[3-9]\\d{9}")) {
+                    rowErrors.add("收件人手机号格式不正确");
+                }
+                if (StringUtils.isBlank(vo.getSenderPhone()) || !vo.getSenderPhone().matches("1[3-9]\\d{9}")) {
+                    rowErrors.add("寄件人手机号格式不正确");
+                }
+                if (StringUtils.isBlank(vo.getReceiverAddress())) {
+                    rowErrors.add("收件地址不能为空");
+                }
+                if (StringUtils.isBlank(vo.getSenderAddress())) {
+                    rowErrors.add("寄件地址不能为空");
+                }
+                if (StringUtils.isBlank(vo.getGoodsName())) {
+                    rowErrors.add("物品名称不能为空");
+                }
+             // 货物重量校验
+                BigDecimal weight = vo.getGoodsWeight();
+                if (weight == null||weight.compareTo(BigDecimal.ZERO) <= 0) {
+                    rowErrors.add("货物重量不能为空,且必须输入大于0的有效数字");
+                }
+              // 货物体积
+                BigDecimal volume = vo.getGoodsVolume();
+                if (volume == null||volume.compareTo(BigDecimal.ZERO) <= 0) {
+                    rowErrors.add("货物体积不能为空,且必须输入大于0的有效数字");
+                }
+                Long goodsQty = vo.getGoodsQty();
+                if (goodsQty == null || goodsQty <= 0) {
+                    rowErrors.add("货物数量不能为空,且必须输入大于0的有效数字");
+                }
+                String productLabel = vo.getProductCode(); // Excel 传的是中文
+                if (!StringUtils.hasText(productLabel)) {
+                    rowErrors.add("产品类型不能为空");
+                }
+
+                String productCode = productMap.get(productLabel);
+                if (productCode == null) {
+                    rowErrors.add("产品类型【" + productLabel + "】不存在,请检查填写是否正确");
+                }
+                vo.setProductCode(productCode);
+
+                /*//还需要校验一下收件地址寄件地址的省市区信息是否合规
+                // ===================== 解析 发件地址 省市区 =====================
+                Map<String, Object> senderParam = new HashMap<>();
+                senderParam.put("param", vo.getSenderAddress());
+                Map<String, Object> senderResultMap = kuaidi100Service.queryIcrExpres(senderParam);
+                Map<String, Object> senderData = (Map<String, Object>) senderResultMap.get("data");
+                List<Map<String, Object>> senderResultList = (List<Map<String, Object>>) senderData.get("result");
+                Map<String, Object> senderResult = senderResultList.get(0);
+                Map<String, Object> senderXzq = (Map<String, Object>) senderResult.get("xzq");
+                // 从 fullName 拆分 省,市,区
+                String senderFullName = (String) senderXzq.get("fullName");
+                String[] senderSplit = senderFullName.split(",");
+                String senderProvince = senderSplit[0];
+                String senderCity = senderSplit[1];
+                String senderDistrict = senderSplit[2];
+                vo.setSenderProvince(senderProvince);
+                vo.setSenderCity(senderCity);
+                vo.setSenderCounty(senderDistrict);
+                 // ===================== 解析 收件地址 省市区 =====================
+                Map<String, Object> receiverParam = new HashMap<>();
+                receiverParam.put("param", vo.getReceiverAddress());
+                Map<String, Object> receiverResultMap = kuaidi100Service.queryIcrExpres(receiverParam);
+                Map<String, Object> receiverData = (Map<String, Object>) receiverResultMap.get("data");
+                List<Map<String, Object>> receiverResultList = (List<Map<String, Object>>) receiverData.get("result");
+                Map<String, Object> receiverResult = receiverResultList.get(0);
+                Map<String, Object> receiverXzq = (Map<String, Object>) receiverResult.get("xzq");
+                // 从 fullName 拆分 省,市,区
+                String receiverFullName = (String) receiverXzq.get("fullName");
+                String[] receiverSplit = receiverFullName.split(",");
+                String receiverProvince = receiverSplit[0];
+                String receiverCity = receiverSplit[1];
+                String receiverDistrict = receiverSplit[2];
+                vo.setReceiverProvince(receiverProvince);
+                vo.setReceiverCity(receiverCity);
+                vo.setReceiverCounty(receiverDistrict);*/
+                //这里拿到生成的id进行前置校验用 如果全部通过的时候  这个还需要用到
+                String orderId = redisIdGenerator.generateUniqueId("RJSD");
+                vo.setWaybillNo(orderId);
+
+                //全部校验完后在进行  下单前置校验
+                BizWaybillOrder bizWaybillOrder = convertToSFBizWaybillOrder(vo, loginUser);
+                JSONObject jsonObject = logisticsOrderService.precheckOrder(bizWaybillOrder);
+                if (!jsonObject.getBooleanValue("success")) {
+                    log.warn("订单前置校验未通过!发件人:{}, 收件人:{}, 原因:{}",
+                            vo.getSenderName(), vo.getReceiverName(), jsonObject.getString("msg"));
+                    rowErrors.add("订单前置校验未通过 原因:"+jsonObject.getString("msg"));
+                }
+                bizWaybillOrderList.add(bizWaybillOrder);
+                if (!rowErrors.isEmpty()) {
+                    errorMsg.append("第").append(row).append("行:")
+                            .append(String.join(",", rowErrors)) // 用逗号拼接所有错误
+                            .append("\n");
+                    continue; // 跳过这一行,不进正常列表
+                }
+            } catch (Exception e) {
+                // 拼接错误:第X行 + 原因
+                errorMsg.append("第").append(row).append("行:").append(e.getMessage()).append("\n");
+            }
+        }
+        result.put("errorMsg",errorMsg);
+        result.put("orederList",bizWaybillOrderList);
+        return result;
+    }
+
+    /**
+     * 将 SfOrderImportDTO 转换为 BizWaybillOrder
+     */
+
+    private BizWaybillOrder convertToSFBizWaybillOrder(SfOrderImportDTO dto, LoginUser loginUser) {
+
+        BizWaybillOrder bizWaybillOrder = new BizWaybillOrder();
+        // 设置订单类型
+        bizWaybillOrder.setOrderType(2);
+        bizWaybillOrder.setWaybillNo(dto.getWaybillNo());
+        // 寄件人信息
+        bizWaybillOrder.setSenderName(dto.getSenderName());
+        bizWaybillOrder.setSenderPhone(dto.getSenderPhone());
+        bizWaybillOrder.setSenderProvince(dto.getSenderProvince());
+        bizWaybillOrder.setSenderCity(dto.getSenderCity());
+        bizWaybillOrder.setSenderCounty(dto.getSenderCounty());
+        bizWaybillOrder.setSenderAddress(dto.getSenderAddress());
+        //收件人信息
+        bizWaybillOrder.setReceiverName(dto.getReceiverName());
+        bizWaybillOrder.setReceiverPhone(dto.getReceiverPhone());
+        bizWaybillOrder.setReceiverProvince(dto.getReceiverProvince());
+        bizWaybillOrder.setReceiverCity(dto.getReceiverCity());
+        bizWaybillOrder.setReceiverCounty(dto.getReceiverCounty());
+        bizWaybillOrder.setReceiverAddress(dto.getReceiverAddress());
+        // 货物信息
+        bizWaybillOrder.setGoodsName(dto.getGoodsName());
+        bizWaybillOrder.setGoodsWeight(dto.getGoodsWeight());
+        bizWaybillOrder.setGoodsVolume(dto.getGoodsVolume());
+        bizWaybillOrder.setGoodsQty(dto.getGoodsQty());
+        // 取件时间
+        bizWaybillOrder.setSendStartTime(dto.getSendStartTime());
+        //结束时间需要计算出来
+        //bizWaybillOrder.setSendEndTime(dto.getSendEndTime());
+        LocalDateTime now = LocalDateTime.now();
+       // 先整点归零
+        LocalDateTime hourStart = now.withMinute(0).withSecond(0).withNano(0);
+       // 下一个整点
+        LocalDateTime nextHour = hourStart.plusHours(1);
+        // 结束再+1
+        LocalDateTime endHour = nextHour.plusHours(1);
+        // 转Date给你原有字段用
+        Date startTime = Date.from(nextHour.atZone(ZoneId.systemDefault()).toInstant());
+        Date endTime = Date.from(endHour.atZone(ZoneId.systemDefault()).toInstant());
+        bizWaybillOrder.setSendStartTime(startTime);
+        bizWaybillOrder.setSendEndTime(endTime);
+        // 产品信息和增值服务
+        bizWaybillOrder.setProductCode(dto.getProductCode());
+
+
+
+        // 2. 构建保价 JSON 格式(你要的结构)
+        Map<String, Object> serviceMap = new HashMap<>();
+        serviceMap.put("isPack", null);                // 固定 null 顺丰没有现在
+        serviceMap.put("guaranteeMoney", dto.getProtecPrice()); // 赋值 Excel 里的报价金额
+        serviceMap.put("isReceiptCollect", null);     // 固定 null   顺丰没有现在
+
+        // 3. 转成 JSON 字符串(如果需要存库)
+        String serviceJson = JSON.toJSONString(serviceMap);
+
+        // 4. 赋值给你的实体(你自己对应字段)
+        bizWaybillOrder.setAddedService(serviceJson);
+
+
+        bizWaybillOrder.setDeptId(loginUser.getSysUser().getDeptId());
+        bizWaybillOrder.setUserId(loginUser.getUserid());
+
+
+
+        return bizWaybillOrder;
+
     }
 
     /**

+ 3 - 1
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/service/impl/SFLogisticsService.java

@@ -140,6 +140,7 @@ public class SFLogisticsService implements LogisticsService {
         List<Map<String,Object>> contactInfoList = new ArrayList<>();
         Map<String,Object> Consignor= new HashMap<>();
         Consignor.put("contactType","1");
+        Consignor.put("contact",bizWaybillOrder.getSenderName());
         Consignor.put("mobile",bizWaybillOrder.getSenderPhone());
         Consignor.put("province",bizWaybillOrder.getSenderProvince());
         Consignor.put("city",bizWaybillOrder.getSenderCity());
@@ -148,6 +149,7 @@ public class SFLogisticsService implements LogisticsService {
         contactInfoList.add(Consignor);
         Map<String,Object> Consignee= new HashMap<>();
         Consignee.put("contactType","2");
+        Consignor.put("contact",bizWaybillOrder.getReceiverName());
         Consignee.put("mobile",bizWaybillOrder.getReceiverPhone());
         Consignee.put("province",bizWaybillOrder.getReceiverProvince());
         Consignee.put("city",bizWaybillOrder.getReceiverCity());
@@ -368,7 +370,7 @@ public class SFLogisticsService implements LogisticsService {
         Map<String, String> params = new HashMap<String, String>();
         String timeStamp = String.valueOf(System.currentTimeMillis());
         Map<String,Object> orderData= new HashMap<>();
-        orderData.put("templateCode","fm_76130_standard_RJAHGDYVIJP8");
+        orderData.put("templateCode","fm_150_standard_RJAHGDYVIJP8");
         orderData.put("version","2.0");
         orderData.put("fileType","pdf");
         orderData.put("sync",true);

+ 4 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java

@@ -1,6 +1,8 @@
 package com.ruoyi.system.mapper;
 
 import java.util.List;
+import java.util.Map;
+
 import org.apache.ibatis.annotations.Param;
 import com.ruoyi.system.api.domain.SysDictData;
 
@@ -92,4 +94,6 @@ public interface SysDictDataMapper
      * @return 结果
      */
     public int updateDictDataType(@Param("oldDictType") String oldDictType, @Param("newDictType") String newDictType);
+
+    List<SysDictData> selectDictDataSfProductList(Map productParam);
 }

+ 4 - 0
jd-logistics-modules/jd-logistics-system/src/main/resources/mapper/logistics/BizWaybillOrderMapper.xml

@@ -50,6 +50,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="deptName"    column="dept_name"    />
         <result property="pdfUrl" column="pdf_url" />
         <result property="totalAmount" column="total_amount" />
+        <result property="batchNum" column="batch_num" />
+
     </resultMap>
 
     <sql id="selectBizWaybillOrderVo">
@@ -165,6 +167,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="userId != null">user_id,</if>
             <if test="productCode != null">product_code,</if>
             <if test="addedService != null">added_service,</if>
+            <if test="batchNum != null">batch_num,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="waybillNo != null">#{waybillNo},</if>
@@ -207,6 +210,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="userId != null">#{userId},</if>
             <if test="productCode != null">#{productCode},</if>
             <if test="addedService != null">#{addedService},</if>
+            <if test="batchNum != null">#{batchNum},</if>
          </trim>
     </insert>
 

+ 23 - 1
jd-logistics-modules/jd-logistics-system/src/main/resources/mapper/system/SysDictDataMapper.xml

@@ -40,7 +40,29 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 		</where>
 		order by dict_sort asc
 	</select>
-	
+
+
+
+	<select id="selectDictDataSfProductList" parameterType="java.util.Map"  resultMap="SysDictDataResult">
+		SELECT
+		b.dict_label,
+		b.dict_value
+		FROM
+		sys_dept_rate a
+		LEFT JOIN sys_dict_data b ON b.dict_value = a.product_type
+
+		<where>
+			a.company_type = '2' and  b.status = 0
+			<if test="dictType != null and dictType != ''">
+				AND b.dict_type = #{dictType}
+			</if>
+			<if test="deptId != null and deptId != ''">
+				AND a.dept_id =#{deptId}
+			</if>
+
+		</where>
+
+	</select>
 	<select id="selectDictDataByType" parameterType="String" resultMap="SysDictDataResult">
 		<include refid="selectDictDataVo"/>
 		where status = '0' and dict_type = #{dictType} order by dict_sort asc