From e8f8d89a4248cd4d0a7138cc2e5a36ea9b136699 Mon Sep 17 00:00:00 2001
From: KKSU <15274802129@163.com>
Date: Tue, 11 Feb 2025 17:03:01 +0800
Subject: [PATCH] feat(mall): 添加订单一键发货和取消发货功能

---
 src/main/resources/templates/index.html                                    |    2 
 src/test/java/cc/mrbird/febs/AgentTest.java                                |  119 -------
 src/main/java/cc/mrbird/febs/mall/controller/ViewSystemController.java     |    5 
 src/main/java/cc/mrbird/febs/mall/service/impl/AdminMallOrderService.java  |  108 +++++++
 src/main/java/cc/mrbird/febs/mall/service/IAdminMallOrderService.java      |    4 
 src/main/java/com/best/javaSdk/ClientService.java                          |   17 +
 src/main/java/com/best/javaSdk/ClientParamEnum.java                        |   34 ++
 src/main/java/com/best/javaSdk/ClientServiceImpl.java                      |   63 ++++
 src/main/java/cc/mrbird/febs/common/enumerates/DataDictionaryEnum.java     |    6 
 src/main/java/cc/mrbird/febs/mall/controller/AdminMallOrderController.java |   24 +
 src/main/java/com/best/javaSdk/ClientParamService.java                     |   39 ++
 src/main/java/cc/mrbird/febs/mall/service/impl/CommonService.java          |   74 ++--
 src/main/resources/templates/febs/views/modules/order/orderList.html       |  201 ++++++++++++++
 src/main/resources/templates/febs/views/modules/system/sender.html         |   97 ++++++
 14 files changed, 638 insertions(+), 155 deletions(-)

diff --git a/src/main/java/cc/mrbird/febs/common/enumerates/DataDictionaryEnum.java b/src/main/java/cc/mrbird/febs/common/enumerates/DataDictionaryEnum.java
index 4f00b80..e9cf56c 100644
--- a/src/main/java/cc/mrbird/febs/common/enumerates/DataDictionaryEnum.java
+++ b/src/main/java/cc/mrbird/febs/common/enumerates/DataDictionaryEnum.java
@@ -1,11 +1,15 @@
 package cc.mrbird.febs.common.enumerates;
 
-import lombok.Data;
 import lombok.Getter;
 
 @Getter
 public enum DataDictionaryEnum {
 
+    // 发货人
+    SENDER_NAME("SENDER_SET", "NAME"),
+    SENDER_MOBILE("SENDER_SET", "MOBILE"),
+    SENDER_ADDRESS("SENDER_SET", "ADDRESS"),
+
     // 发票的通知回调路径
     FP_CALLBACK_URL("FP_CALLBACK_URL", "FP_CALLBACK_URL"),
     //微信订阅模板ID,
diff --git a/src/main/java/cc/mrbird/febs/mall/controller/AdminMallOrderController.java b/src/main/java/cc/mrbird/febs/mall/controller/AdminMallOrderController.java
index 437def5..efb8e51 100644
--- a/src/main/java/cc/mrbird/febs/mall/controller/AdminMallOrderController.java
+++ b/src/main/java/cc/mrbird/febs/mall/controller/AdminMallOrderController.java
@@ -135,6 +135,30 @@
     }
 
     /**
+     * 订单列表-取消发货
+     *
+     * @param id
+     * @return
+     */
+    @GetMapping("cancelDeliver/{id}")
+    @ControllerEndpoint(operation = "订单列表-取消发货", exceptionMessage = "操作失败")
+    public FebsResponse cancelDeliver(@NotNull(message = "{required}") @PathVariable Long id) {
+        return adminMallOrderService.cancelDeliver(id);
+    }
+
+    /**
+     * 订单列表-一键发货
+     *
+     * @param id
+     * @return
+     */
+    @GetMapping("deliverPdfGoods/{id}")
+    @ControllerEndpoint(operation = "订单列表-一键发货", exceptionMessage = "操作失败")
+    public FebsResponse deliverPdfGoods(@NotNull(message = "{required}") @PathVariable Long id) {
+        return adminMallOrderService.deliverPdfGoods(id);
+    }
+
+    /**
      * 订单退款-列表
      *
      * @param mallOrderRefundDto
diff --git a/src/main/java/cc/mrbird/febs/mall/controller/ViewSystemController.java b/src/main/java/cc/mrbird/febs/mall/controller/ViewSystemController.java
index 93e3249..318b86c 100644
--- a/src/main/java/cc/mrbird/febs/mall/controller/ViewSystemController.java
+++ b/src/main/java/cc/mrbird/febs/mall/controller/ViewSystemController.java
@@ -124,4 +124,9 @@
     public String kefu() {
         return FebsUtil.view("modules/system/kefu");
     }
+
+    @GetMapping("sender")
+    public String sender() {
+        return FebsUtil.view("modules/system/sender");
+    }
 }
diff --git a/src/main/java/cc/mrbird/febs/mall/service/IAdminMallOrderService.java b/src/main/java/cc/mrbird/febs/mall/service/IAdminMallOrderService.java
index bae0b6e..dc50e64 100644
--- a/src/main/java/cc/mrbird/febs/mall/service/IAdminMallOrderService.java
+++ b/src/main/java/cc/mrbird/febs/mall/service/IAdminMallOrderService.java
@@ -76,4 +76,8 @@
     IPage<AdminGoodsStatisticsVo> goodsStatistics(MallOrderItem mallOrderItem, QueryRequest request);
 
     FebsResponse deliverGoodsUpdate(DeliverGoodsDto deliverGoodsDto);
+
+    FebsResponse deliverPdfGoods(Long id);
+
+    FebsResponse cancelDeliver(Long id);
 }
diff --git a/src/main/java/cc/mrbird/febs/mall/service/impl/AdminMallOrderService.java b/src/main/java/cc/mrbird/febs/mall/service/impl/AdminMallOrderService.java
index 0f87fc5..2f6a777 100644
--- a/src/main/java/cc/mrbird/febs/mall/service/impl/AdminMallOrderService.java
+++ b/src/main/java/cc/mrbird/febs/mall/service/impl/AdminMallOrderService.java
@@ -2,10 +2,8 @@
 
 import cc.mrbird.febs.common.entity.FebsResponse;
 import cc.mrbird.febs.common.entity.QueryRequest;
-import cc.mrbird.febs.common.enumerates.FlowTypeEnum;
-import cc.mrbird.febs.common.enumerates.MoneyFlowTypeEnum;
-import cc.mrbird.febs.common.enumerates.OrderDeliveryStateEnum;
-import cc.mrbird.febs.common.enumerates.OrderStatusEnum;
+import cc.mrbird.febs.common.enumerates.*;
+import cc.mrbird.febs.common.utils.ValidateEntityUtils;
 import cc.mrbird.febs.mall.dto.*;
 import cc.mrbird.febs.mall.entity.*;
 import cc.mrbird.febs.mall.mapper.*;
@@ -19,12 +17,19 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.best.javaSdk.ClientParamEnum;
+import com.best.javaSdk.ClientParamService;
+import com.best.javaSdk.kdCancelOrderNotify.request.KdCancelOrderNotifyReq;
+import com.best.javaSdk.kdCancelOrderNotify.response.KdCancelOrderNotifyRsp;
+import com.best.javaSdk.kdCreateWaybillOrderPdfNotify.request.*;
+import com.best.javaSdk.kdCreateWaybillOrderPdfNotify.response.KdCreateWaybillOrderPdfNotifyRsp;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
+import java.util.ArrayList;
 import java.util.List;
 
 @Slf4j
@@ -490,6 +495,101 @@
     }
 
     @Override
+    public FebsResponse deliverPdfGoods(Long id) {
+        MallOrderInfo mallOrderInfo = ValidateEntityUtils.ensureColumnReturnEntity(id, MallOrderInfo::getId, mallOrderInfoMapper::selectOne, "订单不存在");
+        ValidateEntityUtils.ensureEqual(mallOrderInfo.getStatus(), OrderStatusEnum.WAIT_SHIPPING.getValue(), "订单不是待发货状态");
+        KdCreateWaybillOrderPdfNotifyReq kdCreateWaybillOrderPdfNotifyReq = new KdCreateWaybillOrderPdfNotifyReq();
+
+        MallMember mallMember = ValidateEntityUtils.ensureColumnReturnEntity(mallOrderInfo.getMemberId(), MallMember::getId, mallMemberMapper::selectOne, "会员不存在");
+
+        kdCreateWaybillOrderPdfNotifyReq.setTxLogisticId(mallOrderInfo.getOrderNo());
+        kdCreateWaybillOrderPdfNotifyReq.setServiceType("1");//服务类型(0-线下下单,1-线上下单)
+        kdCreateWaybillOrderPdfNotifyReq.setSpecial("1");
+
+        //发货人信息
+        Sender sender = new Sender();
+        sender.setName(dataDictionaryCustomMapper.selectDicDataByTypeAndCode(
+                DataDictionaryEnum.SENDER_NAME.getType(),
+                DataDictionaryEnum.SENDER_NAME.getCode()
+        ).getValue());
+        sender.setMobile(dataDictionaryCustomMapper.selectDicDataByTypeAndCode(
+                DataDictionaryEnum.SENDER_MOBILE.getType(),
+                DataDictionaryEnum.SENDER_MOBILE.getCode()
+        ).getValue());
+        sender.setAddress(dataDictionaryCustomMapper.selectDicDataByTypeAndCode(
+                DataDictionaryEnum.SENDER_ADDRESS.getType(),
+                DataDictionaryEnum.SENDER_ADDRESS.getCode()
+        ).getValue());
+        kdCreateWaybillOrderPdfNotifyReq.setSender(sender);
+
+        //收件人
+        Receiver receiver = new Receiver();
+        receiver.setName(mallMember.getRealName());
+        receiver.setMobile(mallMember.getPhone());
+        receiver.setAddress(mallOrderInfo.getAddress());
+        kdCreateWaybillOrderPdfNotifyReq.setReceiver(receiver);
+
+        //包裹
+        Items items = new Items();
+        ArrayList<Item> itemList = new ArrayList<>();
+        List<MallOrderItem> mallOrderItemList = ValidateEntityUtils
+                .ensureColumnReturnEntityList(id, MallOrderItem::getOrderId, mallOrderItemMapper::selectList, "订单不存在");
+        StringBuffer itemName = new StringBuffer();
+        itemName.append("商品:");
+        mallOrderItemList.forEach(mallOrderItem -> {
+            itemName.append(mallOrderItem.getGoodsName()+"-"+mallOrderItem.getSkuName());
+        });
+        Item item = new Item();
+        item.setItemName(itemName.toString());
+        itemList.add(item);
+        items.setItem(itemList);
+        kdCreateWaybillOrderPdfNotifyReq.setItems(items);
+
+        kdCreateWaybillOrderPdfNotifyReq.setPiece(1);
+        KdCreateWaybillOrderPdfNotifyRsp pdfOrder = ClientParamService.getInstance(ClientParamEnum.TEST.name()).createPdfOrder(kdCreateWaybillOrderPdfNotifyReq);
+
+        ValidateEntityUtils.ensureEqual(pdfOrder.getResult(), true, "一键发货失败,创建PDF电子面单异常");
+        //更新发货状态
+        mallOrderInfoMapper.updateOrderStateAndDeliveryState(
+                mallOrderInfo.getId(),
+                OrderStatusEnum.WAIT_FINISH.getValue(),
+                OrderDeliveryStateEnum.DELIVERY_ING.getValue());
+
+        MallExpressInfo mallExpressInfo = new MallExpressInfo();
+        mallExpressInfo.setMemberId(mallOrderInfo.getMemberId());
+        mallExpressInfo.setOrderId(mallOrderInfo.getId());
+        mallExpressInfo.setExpressNo(pdfOrder.getMailNo());
+        mallExpressInfo.setExpressCom("Best Logistic");
+        mallExpressInfo.setExpressCode("Best Logistic");
+        mallExpressInfoMapper.insert(mallExpressInfo);
+
+        return new FebsResponse().success().data(pdfOrder);
+    }
+
+    @Override
+    public FebsResponse cancelDeliver(Long id) {
+
+        MallOrderInfo mallOrderInfo = ValidateEntityUtils.ensureColumnReturnEntity(id, MallOrderInfo::getId, mallOrderInfoMapper::selectOne, "订单不存在");
+        ValidateEntityUtils.ensureEqual(mallOrderInfo.getStatus(), OrderStatusEnum.WAIT_FINISH.getValue(), "订单不是待收货状态");
+
+        KdCancelOrderNotifyReq kdCancelOrderNotifyReq = new KdCancelOrderNotifyReq();
+        kdCancelOrderNotifyReq.setTxLogisticId(mallOrderInfo.getOrderNo());
+        kdCancelOrderNotifyReq.setReason("Don't want to buy");
+        KdCancelOrderNotifyRsp kdCancelOrderNotifyRsp = ClientParamService.getInstance(ClientParamEnum.TEST.name()).cancelOrder(kdCancelOrderNotifyReq);
+        ValidateEntityUtils.ensureEqual(kdCancelOrderNotifyRsp.getResult(), true, "取消发货失败");
+
+        //更新发货状态
+        mallOrderInfoMapper.updateOrderStateAndDeliveryState(
+                mallOrderInfo.getId(),
+                OrderStatusEnum.WAIT_SHIPPING.getValue(),
+                OrderDeliveryStateEnum.DELIVERY_WAIT.getValue());
+
+        List<MallExpressInfo> mallExpressInfoList = ValidateEntityUtils.ensureColumnReturnEntityList(mallOrderInfo.getId(), MallExpressInfo::getOrderId, mallExpressInfoMapper::selectList, "未查询到物流信息");
+        mallExpressInfoList.forEach(mallExpressInfo -> mallExpressInfoMapper.deleteById(mallExpressInfo.getId()));
+        return new FebsResponse().success().message("取消发货成功,请重新发货");
+    }
+
+    @Override
     public void deliverGoodsByOrderNo(DeliverGoodsDto deliverGoodsDto) {
         MallOrderInfo mallOrderInfo = mallOrderInfoMapper.selectByOrderNo(deliverGoodsDto.getOrderNo());
         if (mallOrderInfo == null) {
diff --git a/src/main/java/cc/mrbird/febs/mall/service/impl/CommonService.java b/src/main/java/cc/mrbird/febs/mall/service/impl/CommonService.java
index b890d8d..d70d969 100644
--- a/src/main/java/cc/mrbird/febs/mall/service/impl/CommonService.java
+++ b/src/main/java/cc/mrbird/febs/mall/service/impl/CommonService.java
@@ -11,15 +11,13 @@
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson.JSONObject;
-import com.best.javaSdk.Client;
-import com.best.javaSdk.kdTraceQuery.request.KdTraceQueryReq;
-import com.best.javaSdk.kdTraceQuery.request.MailNos;
+import com.best.javaSdk.ClientParamEnum;
+import com.best.javaSdk.ClientParamService;
 import com.best.javaSdk.kdTraceQuery.response.KdTraceQueryRsp;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -107,39 +105,43 @@
         dataDictionaryCustomMapper.insert(dic);
     }
 
+//    @Override
+//    public KdTraceQueryRsp checkTraceInfo(ApiCheckTraceInfoDto checkTraceInfoDto) {
+//        /**
+//         * 正式环境
+//         * MY_LEADING 生产环境参数
+//         *               partnerID   : MY_LEADING
+//         *               partnerKey  :  ER5DFRT320D4ed6FAFs3G410Fs977
+//         *               Endpoint    :http://sgp-seaedi.800best.com/Malaysia/kdapi/api/process
+//         */
+//        String url = "http://sgp-seaedi.800best.com/Malaysia/kdapi/api/process";
+//        String partnerID = "MY_LEADING";
+//        String partnerKey = "ER5DFRT320D4ed6FAFs3G410Fs977";
+//        String format = "JSON";
+//        /**
+//         * 测试环境
+//         *  测试物流编号 66660451238000
+//         */
+////        String url = "http://sea-edi-hxtest.800best.com/Malaysia/kdapi/api/process";
+////        String partnerID = "M_TEST";
+////        String partnerKey = "TEST12345";
+////        String format = "JSON";
+//
+//        Client client = new Client(url, partnerID, partnerKey, format);
+//
+//        KdTraceQueryReq tdTraceQueryReq = new KdTraceQueryReq();
+//        MailNos mailNos = new MailNos();
+//        List<String> mailNo = new ArrayList<>();
+//        mailNo.add(checkTraceInfoDto.getTraceNo());
+//        mailNos.setMailNo(mailNo);
+//        tdTraceQueryReq.setMailNos(mailNos);
+//        tdTraceQueryReq.setLangType("zh-CN");
+//
+//        KdTraceQueryRsp kdTraceQueryRsp = client.executed(tdTraceQueryReq);
+//        return kdTraceQueryRsp;
+//    }
     @Override
     public KdTraceQueryRsp checkTraceInfo(ApiCheckTraceInfoDto checkTraceInfoDto) {
-        /**
-         * 正式环境
-         * MY_LEADING 生产环境参数
-         *               partnerID   : MY_LEADING
-         *               partnerKey  :  ER5DFRT320D4ed6FAFs3G410Fs977
-         *               Endpoint    :http://sgp-seaedi.800best.com/Malaysia/kdapi/api/proces
-         */
-        String url = "http://sgp-seaedi.800best.com/Malaysia/kdapi/api/process";
-        String partnerID = "MY_LEADING";
-        String partnerKey = "ER5DFRT320D4ed6FAFs3G410Fs977";
-        String format = "JSON";
-        /**
-         * 测试环境
-         *  测试物流编号 66660451238000
-         */
-//        String url = "http://sea-edi-hxtest.800best.com/Malaysia/kdapi/api/process";
-//        String partnerID = "M_TEST";
-//        String partnerKey = "TEST12345";
-//        String format = "JSON";
-
-        Client client = new Client(url, partnerID, partnerKey, format);
-
-        KdTraceQueryReq tdTraceQueryReq = new KdTraceQueryReq();
-        MailNos mailNos = new MailNos();
-        List<String> mailNo = new ArrayList<>();
-        mailNo.add(checkTraceInfoDto.getTraceNo());
-        mailNos.setMailNo(mailNo);
-        tdTraceQueryReq.setMailNos(mailNos);
-        tdTraceQueryReq.setLangType("zh-CN");
-
-        KdTraceQueryRsp kdTraceQueryRsp = client.executed(tdTraceQueryReq);
-        return kdTraceQueryRsp;
+        return ClientParamService.getInstance(ClientParamEnum.PRD.name()).checkTraceInfo(checkTraceInfoDto);
     }
 }
diff --git a/src/main/java/com/best/javaSdk/ClientParamEnum.java b/src/main/java/com/best/javaSdk/ClientParamEnum.java
new file mode 100644
index 0000000..5303819
--- /dev/null
+++ b/src/main/java/com/best/javaSdk/ClientParamEnum.java
@@ -0,0 +1,34 @@
+package com.best.javaSdk;
+
+import lombok.Getter;
+
+@Getter
+public enum ClientParamEnum {
+
+    TEST(
+            "http://sea-edi-hxtest.800best.com/Malaysia/kdapi/api/process",
+            "M_TEST",
+            "TEST12345",
+            "JSON"
+    ),
+
+    PRD(
+            "http://sgp-seaedi.800best.com/Malaysia/kdapi/api/process",
+            "MY_LEADING",
+            "ER5DFRT320D4ed6FAFs3G410Fs977",
+            "JSON"
+    );
+
+    private String url;
+    private String partnerID;
+    private String partnerKey;
+    private String messageFormat;
+
+    ClientParamEnum(String url, String partnerID, String partnerKey, String messageFormat) {
+        this.url = url;
+        this.partnerID = partnerID;
+        this.partnerKey = partnerKey;
+        this.messageFormat = messageFormat;
+    }
+
+}
diff --git a/src/main/java/com/best/javaSdk/ClientParamService.java b/src/main/java/com/best/javaSdk/ClientParamService.java
new file mode 100644
index 0000000..b8b18ad
--- /dev/null
+++ b/src/main/java/com/best/javaSdk/ClientParamService.java
@@ -0,0 +1,39 @@
+package com.best.javaSdk;
+
+import cc.mrbird.febs.common.exception.FebsException;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+public class ClientParamService {
+
+    private final static Map<String, ClientServiceImpl> client_map = new HashMap<>();
+
+    static {
+        for (ClientParamEnum clientParamEnum : ClientParamEnum.values()) {
+            client_map.put(
+                    clientParamEnum.name(),
+                    new ClientServiceImpl(
+                            clientParamEnum.getUrl(),
+                            clientParamEnum.getPartnerID(),
+                            clientParamEnum.getPartnerKey(),
+                            clientParamEnum.getMessageFormat()));
+        }
+    }
+
+    private ClientParamService() {
+    }
+
+    public final static ClientParamService INSTANCE = new ClientParamService();
+
+    public static ClientService getInstance(String type) {
+        ClientServiceImpl clientService = client_map.get(type);
+        if (clientService == null) {
+            throw new FebsException("参数错误");
+        }
+
+        return clientService;
+    }
+}
diff --git a/src/main/java/com/best/javaSdk/ClientService.java b/src/main/java/com/best/javaSdk/ClientService.java
new file mode 100644
index 0000000..62fafdb
--- /dev/null
+++ b/src/main/java/com/best/javaSdk/ClientService.java
@@ -0,0 +1,17 @@
+package com.best.javaSdk;
+
+import cc.mrbird.febs.mall.dto.ApiCheckTraceInfoDto;
+import com.best.javaSdk.kdCancelOrderNotify.request.KdCancelOrderNotifyReq;
+import com.best.javaSdk.kdCancelOrderNotify.response.KdCancelOrderNotifyRsp;
+import com.best.javaSdk.kdCreateWaybillOrderPdfNotify.request.KdCreateWaybillOrderPdfNotifyReq;
+import com.best.javaSdk.kdCreateWaybillOrderPdfNotify.response.KdCreateWaybillOrderPdfNotifyRsp;
+import com.best.javaSdk.kdTraceQuery.response.KdTraceQueryRsp;
+
+public interface ClientService {
+
+    KdTraceQueryRsp checkTraceInfo(ApiCheckTraceInfoDto checkTraceInfoDto);
+
+    KdCreateWaybillOrderPdfNotifyRsp createPdfOrder(KdCreateWaybillOrderPdfNotifyReq kdCreateWaybillOrderPdfNotifyReq);
+
+    KdCancelOrderNotifyRsp cancelOrder(KdCancelOrderNotifyReq kdCancelOrderNotifyReq);
+}
diff --git a/src/main/java/com/best/javaSdk/ClientServiceImpl.java b/src/main/java/com/best/javaSdk/ClientServiceImpl.java
new file mode 100644
index 0000000..82d195d
--- /dev/null
+++ b/src/main/java/com/best/javaSdk/ClientServiceImpl.java
@@ -0,0 +1,63 @@
+package com.best.javaSdk;
+
+import cc.mrbird.febs.mall.dto.ApiCheckTraceInfoDto;
+import cn.hutool.json.JSONUtil;
+import com.best.javaSdk.kdCancelOrderNotify.request.KdCancelOrderNotifyReq;
+import com.best.javaSdk.kdCancelOrderNotify.response.KdCancelOrderNotifyRsp;
+import com.best.javaSdk.kdCreateWaybillOrderPdfNotify.request.KdCreateWaybillOrderPdfNotifyReq;
+import com.best.javaSdk.kdCreateWaybillOrderPdfNotify.response.KdCreateWaybillOrderPdfNotifyRsp;
+import com.best.javaSdk.kdTraceQuery.request.KdTraceQueryReq;
+import com.best.javaSdk.kdTraceQuery.request.MailNos;
+import com.best.javaSdk.kdTraceQuery.response.KdTraceQueryRsp;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Slf4j
+public class ClientServiceImpl implements ClientService{
+
+    private Client client;
+    private String url;
+    private String partnerID;
+    private String partnerKey;
+    private String messageFormat;
+
+    public ClientServiceImpl(String url, String partnerID, String partnerKey, String messageFormat) {
+        this.url = url;
+        this.partnerID = partnerID;
+        this.partnerKey = partnerKey;
+        this.messageFormat = messageFormat;
+
+        client = new Client(url, partnerID, partnerKey, messageFormat);
+    }
+
+    @Override
+    public KdTraceQueryRsp checkTraceInfo(ApiCheckTraceInfoDto checkTraceInfoDto) {
+        KdTraceQueryReq tdTraceQueryReq = new KdTraceQueryReq();
+        MailNos mailNos = new MailNos();
+        List<String> mailNo = new ArrayList<>();
+        mailNo.add(checkTraceInfoDto.getTraceNo());
+        mailNos.setMailNo(mailNo);
+        tdTraceQueryReq.setMailNos(mailNos);
+        tdTraceQueryReq.setLangType("zh-CN");
+        KdTraceQueryRsp kdTraceQueryRsp = client.executed(tdTraceQueryReq);
+        return kdTraceQueryRsp;
+    }
+
+    @Override
+    public KdCreateWaybillOrderPdfNotifyRsp createPdfOrder(KdCreateWaybillOrderPdfNotifyReq kdCreateWaybillOrderPdfNotifyReq) {
+        KdCreateWaybillOrderPdfNotifyRsp executed = client.executed(kdCreateWaybillOrderPdfNotifyReq);
+        log.info("executed:{}", JSONUtil.parseObj(executed));
+        return executed;
+    }
+
+    @Override
+    public KdCancelOrderNotifyRsp cancelOrder(KdCancelOrderNotifyReq kdCancelOrderNotifyReq) {
+        KdCancelOrderNotifyRsp executed = client.executed(kdCancelOrderNotifyReq);
+        log.info("executed:{}", JSONUtil.parseObj(executed));
+        return executed;
+    }
+
+
+}
diff --git a/src/main/resources/templates/febs/views/modules/order/orderList.html b/src/main/resources/templates/febs/views/modules/order/orderList.html
index c2dbb7e..ee8d91e 100644
--- a/src/main/resources/templates/febs/views/modules/order/orderList.html
+++ b/src/main/resources/templates/febs/views/modules/order/orderList.html
@@ -356,6 +356,16 @@
                     cancelOrder(data.id);
                 });
             }
+            if (layEvent === 'deliverPdfGoods') {
+                febs.modal.confirm('一键发货', '确认一键发货?', function () {
+                    deliverPdfGoods(data.id);
+                });
+            }
+            if (layEvent === 'cancelDeliver') {
+                febs.modal.confirm('取消发货', '确认取消发货?', function () {
+                    cancelDeliver(data.id);
+                });
+            }
             if (layEvent === 'seePayImage') {
                 var t = $view.find('#seePayImage'+data.id+'');
                 //页面层
@@ -373,12 +383,195 @@
             }
         });
 
+        function cancelDeliver(id) {
+            febs.get(ctx + 'admin/order/cancelDeliver/' + id, null, function (data) {
+                febs.alert.success(data.message);
+                $query.click();
+            });
+        }
+
         function cancelOrder(id) {
             febs.get(ctx + 'admin/order/cancelOrder/' + id, null, function () {
                 febs.alert.success('操作成功');
                 $query.click();
             });
         }
+
+        function deliverPdfGoods(id) {
+            febs.get(ctx + 'admin/order/deliverPdfGoods/' + id, null, function (e) {
+                if (e.code == 200) {
+                    // 创建弹层容器
+                    const content = `
+                                        <div style="position:relative;min-height:500px">
+                                          <div id="pdfContainer" style="margin:20px auto;max-width:800px"></div>
+                                          <div class="layui-form" style="text-align:center;margin-top:20px">
+                                            <button class="layui-btn layui-btn-normal" onclick="printPdf()">打印</button>
+                                          </div>
+                                        </div>
+                                      `;
+
+                    // 显示弹层
+                    const index = layer.open({
+                        type: 1,
+                        title: "电子面单",
+                        content: content,
+                        area: ['90%', '90%'],
+                        success: function() {
+                            // 渲染PDF
+                            renderPdf(e.data.pdfStream);
+                        }
+                    });
+                    // 存储当前PDF数据
+                    window.currentPDF = e.data.pdfStream;
+                } else {
+                    febs.alert.warn(e.message);
+                }
+            });
+            $query.click();
+        }
+
+        // PDF渲染函数
+        function renderPdf(base64Data) {
+            // 清理容器
+            const container = document.getElementById('pdfContainer');
+            container.innerHTML = '';
+
+            // Base64转Blob
+            const byteCharacters = atob(base64Data);
+            const byteNumbers = new Array(byteCharacters.length);
+            for (let i = 0; i < byteCharacters.length; i++) {
+                byteNumbers[i] = byteCharacters.charCodeAt(i);
+            }
+            const byteArray = new Uint8Array(byteNumbers);
+            const blob = new Blob([byteArray], {type: 'application/pdf'});
+
+            // 生成临时URL
+            const url = URL.createObjectURL(blob);
+
+            // 使用PDF.js渲染
+            pdfjsLib.getDocument(url).promise.then(pdf => {
+                // 循环渲染所有页面
+                for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
+                    pdf.getPage(pageNum).then(page => {
+                        const scale = 1.5;
+                        const viewport = page.getViewport({scale: scale});
+                        const canvas = document.createElement('canvas');
+                        const context = canvas.getContext('2d');
+                        canvas.height = viewport.height;
+                        canvas.width = viewport.width;
+
+                        // 创建页面容器
+                        const pageDiv = document.createElement('div');
+                        pageDiv.className = 'pdf-page';
+                        pageDiv.style.margin = '10px 0';
+                        container.appendChild(pageDiv);
+
+                        // 渲染到Canvas
+                        page.render({
+                            canvasContext: context,
+                            viewport: viewport
+                        }).promise.then(() => {
+                            pageDiv.appendChild(canvas);
+                        });
+                    });
+                }
+            });
+        }
+
+        // 打印函数
+        window.printPdf = function() {
+            let base64Image = window.currentPDF;
+            const printWindow = window.open('', '_blank');
+            printWindow.document.write(`
+                                        <html>
+                                          <head>
+                                            <title>电子面单打印</title>
+                                            <style>
+                                              body { margin: 0; padding: 20px }
+                                              canvas {
+                                                width: 100%!important;
+                                                height: auto!important;
+                                                page-break-after: always;
+                                              }
+                                            </style>
+                                          </head>
+                                          <body>
+                                            <iframe src="data:application/pdf;base64,`+base64Image+`" width="100%" height="100%"></iframe>
+                                          </body>
+                                        </html>
+                                      `);
+            printWindow.document.close();
+            printWindow.onload = function() {
+                printWindow.print();
+                printWindow.close();
+            }
+        }
+
+        // function deliverPdfGoods(id) {
+        //     febs.get(ctx + 'admin/order/deliverPdfGoods/' + id, null, function (e) {
+        //         if(e.code == 200){
+        //             console.info(e.data);
+        //             console.info(e.data.pdfStream);
+        //             console.info(e.data.result);
+        //             layer.open({
+        //                 type: 1,
+        //                 title: "电子面单",
+        //                 skin: 'layui-layer-rim', //加上边框
+        //                 area: ['100%', '100%'], //宽高
+        //                 shadeClose: true, //开启遮罩关闭
+        //                 end: function (index, layero) {
+        //                     return false;
+        //                 },
+        //                 content: '<div class="layui-card-body" id="pdfViewer"></div>'
+        //             });
+        //             if(e.data.pdfStream === null){
+        //
+        //                 renderPdf(e.data.pdfStreamList);
+        //             }
+        //             renderPdf(e.data.pdfStream);
+        //         }else{
+        //             febs.alert.warn(e.message);
+        //         }
+        //         $query.click();
+        //     });
+        // }
+
+        // 渲染 PDF 到页面
+        // function renderPdf(base64Data) {
+        //     // Base64 转 Blob
+        //     const byteCharacters = atob(base64Data);
+        //     const byteNumbers = new Array(byteCharacters.length);
+        //     for (let i = 0; i < byteCharacters.length; i++) {
+        //         byteNumbers[i] = byteCharacters.charCodeAt(i);
+        //     }
+        //     const byteArray = new Uint8Array(byteNumbers);
+        //     const blob = new Blob([byteArray], {type: 'application/pdf'});
+        //
+        //     // 生成临时 URL
+        //     const url = URL.createObjectURL(blob);
+        //
+        //     // 使用 PDF.js 渲染
+        //     pdfjsLib.getDocument(url).promise.then(function(pdf) {
+        //         pdf.getPage(1).then(function(page) {
+        //             const scale = 1.5;
+        //             const viewport = page.getViewport({scale: scale});
+        //             const canvas = document.createElement('canvas');
+        //             const context = canvas.getContext('2d');
+        //             canvas.height = viewport.height;
+        //             canvas.width = viewport.width;
+        //
+        //             // 将 PDF 页面渲染到 Canvas
+        //             page.render({
+        //                 canvasContext: context,
+        //                 viewport: viewport
+        //             }).promise.then(function() {
+        //                 $('#pdfViewer').html(canvas);
+        //             });
+        //         });
+        //     }).catch(function(error) {
+        //         layer.msg('PDF渲染失败: ' + error.message, {icon: 2});
+        //     });
+        // }
 
         // 查询按钮
         $query.on('click', function () {
@@ -456,11 +649,13 @@
                         {title: '操作',
                             templet: function (d) {
                                 if(d.status === 2){
-                                    return '<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="seeOrder" shiro:hasPermission="user:update">详情</button>'
-                                    +'<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="deliverGoods" shiro:hasPermission="user:update">发货</button>'
+                                    return '<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="deliverPdfGoods" shiro:hasPermission="user:update">一键发货</button>'
+                                    +'<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="seeOrder" shiro:hasPermission="user:update">详情</button>'
+                                    // +'<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="deliverGoods" shiro:hasPermission="user:update">发货</button>'
                                 }else if(d.status === 3){
                                     return '<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="seeOrder" shiro:hasPermission="user:update">详情</button>'
-                                    +'<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="updateDeliver" shiro:hasPermission="user:update">修改物流信息</button>'
+                                        +'<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="cancelDeliver" shiro:hasPermission="user:update">取消发货</button>'
+                                    // +'<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="updateDeliver" shiro:hasPermission="user:update">修改物流信息</button>'
                                 }else{
                                     return '<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="seeOrder" shiro:hasPermission="user:update">详情</button>'
                                 }
diff --git a/src/main/resources/templates/febs/views/modules/system/sender.html b/src/main/resources/templates/febs/views/modules/system/sender.html
new file mode 100644
index 0000000..e8ee3b0
--- /dev/null
+++ b/src/main/resources/templates/febs/views/modules/system/sender.html
@@ -0,0 +1,97 @@
+<div class="layui-fluid layui-anim febs-anim" id="sender-setting" lay-title="发货地址">
+    <div class="layui-row layui-col-space8 febs-container">
+        <form class="layui-form" action="" lay-filter="sender-setting-form">
+            <div class="layui-card">
+                <div class="layui-card-body flex" id="cardBodySender">
+
+                </div>
+
+                <div class="layui-card-footer">
+                    <button class="layui-btn layui-btn-normal save-btn" lay-submit="" lay-filter="sender-setting-form-submit" id="submit">保存</button>
+                </div>
+            </div>
+        </form>
+    </div>
+</div>
+<style>
+    .layui-form-label {
+        width: 120px;
+    }
+
+    .layui-form-item .layui-input-block {
+        margin-left: 150px;
+    }
+
+    .layui-table-form .layui-form-item {
+        margin-bottom: 20px !important;
+    }
+    .flex{
+        display: flex;
+        flex-wrap: wrap;
+    }
+    .save-btn{
+        display: block;
+        margin: 0 auto;
+    }
+    .layui-upload-list{
+        height: 200px;
+    }
+</style>
+<script type="text/html" id="senderOperate">
+    {{#  layui.each(d, function(index, item){ }}
+    <div class="layui-form-item">
+        <label class="layui-form-label febs-form-item-require">{{item.description}}:</label>
+        <input type="text" lay-verify="required" name="{{item.code}}"
+               autoComplete="off" value="{{item.value}}" class="layui-input">
+    </div>
+    {{# }) }}
+</script>
+<script data-th-inline="javascript" type="text/javascript">
+    layui.use(['dropdown', 'jquery', 'validate', 'febs', 'form', 'eleTree', 'laytpl', 'upload'], function () {
+        var $ = layui.jquery,
+            febs = layui.febs,
+            form = layui.form,
+            validate = layui.validate
+            , templateHtml = senderOperate.innerHTML
+            , $cardBodySender = $("#cardBodySender")
+            , laytpl = layui.laytpl
+            , upload = layui.upload
+            , $view = $('#sender-setting');
+
+        form.verify(validate);
+        form.render();
+
+        dicDataReq("SENDER_SET");
+        function dicDataReq(type) {
+            $cardBodySender.empty();
+            $.get(ctx + 'admin/common/findDicByType/' + type, function (r) {
+                if (r.code === 200) {
+                    var data = r.data;
+                    laytpl(templateHtml).render(data, function(html) {
+                        $cardBodySender.append(html);
+                    })
+                }
+            });
+        }
+
+        form.on('submit(sender-setting-form-submit)', function (data) {
+            $.ajax({
+                'url':ctx + 'admin/system/bonusSystemSetting',
+                'type':'post',
+                'dataType':'json',
+                'headers' : {'Content-Type' : 'application/json;charset=utf-8'},
+                'traditional': true,
+                'data':JSON.stringify(data.field),
+                'success':function (data) {
+                    if (data.code == 200) {
+                        febs.alert.success(data.message);
+                    }
+                },
+                'error':function () {
+                    febs.alert.warn('服务器繁忙');
+                }
+            })
+            return false;
+        });
+    });
+</script>
\ No newline at end of file
diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html
index e3e3270..0b3a980 100644
--- a/src/main/resources/templates/index.html
+++ b/src/main/resources/templates/index.html
@@ -25,7 +25,7 @@
 <!--    <script src="https://unpkg.com/@wangeditor/editor@latest/dist/index.js"></script>-->
     <script src="http://gosspublic.alicdn.com/aliyun-oss-sdk-6.17.0.min.js"></script>
     <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/wangeditor@latest/dist/wangEditor.min.js" ></script>
-
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.6.347/pdf.min.js"></script>
     <style type="text/css">
         ::-webkit-scrollbar {
             height: 20px !important;
diff --git a/src/test/java/cc/mrbird/febs/AgentTest.java b/src/test/java/cc/mrbird/febs/AgentTest.java
index b3cf218..38d8b6f 100644
--- a/src/test/java/cc/mrbird/febs/AgentTest.java
+++ b/src/test/java/cc/mrbird/febs/AgentTest.java
@@ -1,127 +1,26 @@
 package cc.mrbird.febs;
 
-import cc.mrbird.febs.common.enumerates.FlowTypeEnum;
-import cc.mrbird.febs.common.enumerates.MoneyFlowTypeEnum;
-import cc.mrbird.febs.mall.entity.*;
-import cc.mrbird.febs.mall.mapper.*;
-import cc.mrbird.febs.mall.service.IMallMoneyFlowService;
-import cc.mrbird.febs.pay.model.RefundStatus;
-import cc.mrbird.febs.pay.util.FiuuRefundUtil;
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.date.DateUtil;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import cc.mrbird.febs.mall.dto.ApiCheckTraceInfoDto;
+import cn.hutool.json.JSONUtil;
+import com.best.javaSdk.ClientParamEnum;
+import com.best.javaSdk.ClientParamService;
+import com.best.javaSdk.kdTraceQuery.response.KdTraceQueryRsp;
 import lombok.extern.slf4j.Slf4j;
 import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
-
-import java.util.List;
 
 @Slf4j
 @SpringBootTest
 public class AgentTest {
 
-    @Autowired
-    private MallActivityMapper mallActivityMapper;
-
-    @Autowired
-    private MallRefundMapper mallRefundMapper;
-
-    @Autowired
-    private MallOrderInfoMapper mallOrderInfoMapper;
-
-    @Autowired
-    private MallOrderItemMapper mallOrderItemMapper;
-
-    @Autowired
-    private MallGoodsSkuMapper mallGoodsSkuMapper;
-
-    @Autowired
-    private MallGoodsMapper mallGoodsMapper;
-
-    @Autowired
-    private FiuuRefundUtil fiuuRefundUtil;
-
-    @Autowired
-    private IMallMoneyFlowService mallMoneyFlowService;
     @Test
     public void refundJob() {
-        LambdaQueryWrapper<MallRefundEntity> mallOrderRefundLambdaQueryWrapper = new LambdaQueryWrapper<>();
-        mallOrderRefundLambdaQueryWrapper.eq(MallRefundEntity::getState, 3);
-        mallOrderRefundLambdaQueryWrapper.eq(MallRefundEntity::getType, 1);
-        List<MallRefundEntity> mallRefundEntities = mallRefundMapper.selectList(mallOrderRefundLambdaQueryWrapper);
-        if(CollUtil.isEmpty(mallRefundEntities)){
-            return;
-        }
-        mallRefundEntities.forEach(mallRefundEntity -> {
-            processRefund(mallRefundEntity);
-        });
+        ApiCheckTraceInfoDto apiCheckTraceInfoDto = new ApiCheckTraceInfoDto();
+        apiCheckTraceInfoDto.setTraceNo("60850712015414");
+        KdTraceQueryRsp traceQueryRsp = ClientParamService.getInstance(ClientParamEnum.PRD.name()).checkTraceInfo(apiCheckTraceInfoDto);
+        log.info("traceQueryRsp:{}", JSONUtil.parseObj(traceQueryRsp));
     }
 
-    private void processRefund(MallRefundEntity mallRefundEntity) {
-        try {
-            MallOrderInfo mallOrderInfo = mallOrderInfoMapper.selectById(mallRefundEntity.getOrderId());
-            MallOrderItem mallOrderItem = mallOrderItemMapper.selectById(mallRefundEntity.getItemId());
-            MallGoodsSku mallGoodsSku = mallGoodsSkuMapper.selectById(mallOrderItem.getSkuId());
 
-            String txnId = mallOrderInfo.getPayOrderNo();
-            RefundStatus status = fiuuRefundUtil.pollRefundStatus(txnId);
-
-            switch (status.getStatus()) {
-                case "success":
-                    updateOnSuccess(mallRefundEntity, mallOrderInfo, mallOrderItem, mallGoodsSku);
-                    break;
-                case "rejected":
-                    updateOnRejected(mallRefundEntity, mallOrderItem);
-                    break;
-                default:
-                    log.warn("未知状态:{}", status.getStatus());
-            }
-        } catch (Exception e) {
-            log.error("处理退款失败: {}", e.getMessage(), e);
-        }
-    }
-
-    private void updateOnSuccess(MallRefundEntity mallRefundEntity, MallOrderInfo mallOrderInfo, MallOrderItem mallOrderItem, MallGoodsSku mallGoodsSku) {
-        mallOrderItem.setState(3);
-        mallOrderItemMapper.updateById(mallOrderItem);
-
-        MallGoods mallGoods = mallGoodsMapper.selectById(mallOrderItem.getGoodsId());
-        mallGoods.setStock(mallGoods.getStock() + mallOrderItem.getCnt());
-        mallGoods.setVolume(mallGoods.getVolume() - mallOrderItem.getCnt());
-        mallGoodsMapper.updateById(mallGoods);
-
-        mallGoodsSku.setStock(mallGoodsSku.getStock() + mallOrderItem.getCnt());
-        mallGoodsSku.setSkuVolume(mallGoodsSku.getSkuVolume() - mallOrderItem.getCnt());
-        mallGoodsSkuMapper.updateById(mallGoodsSku);
-
-        mallRefundEntity.setState(1);
-        mallRefundEntity.setUpdatedTime(DateUtil.date());
-        mallRefundMapper.updateById(mallRefundEntity);
-
-        mallMoneyFlowService.addMoneyFlow(
-                mallOrderInfo.getMemberId(),
-                mallRefundEntity.getAmount(),
-                MoneyFlowTypeEnum.WECHAT_REFUND.getValue(),
-                mallOrderInfo.getOrderNo(),
-                FlowTypeEnum.WECHAT.getValue(),
-                "FIUU退款",
-                2);
-
-        List<MallOrderItem> mallOrderItemList = mallOrderItemMapper.selectListByNotInStateAndOrderId(3, mallRefundEntity.getOrderId());
-        if (CollUtil.isEmpty(mallOrderItemList)) {
-            MallOrderInfo mallOrderRefund = mallOrderInfoMapper.selectById(mallRefundEntity.getOrderId());
-            mallOrderRefund.setStatus(6);
-            mallOrderInfoMapper.updateById(mallOrderRefund);
-        }
-    }
-
-    private void updateOnRejected(MallRefundEntity mallRefundEntity, MallOrderItem mallOrderItem) {
-        mallOrderItem.setState(1);
-        mallOrderItemMapper.updateById(mallOrderItem);
-
-        mallRefundEntity.setState(2);
-        mallRefundMapper.updateById(mallRefundEntity);
-    }
 
 }

--
Gitblit v1.9.1