Browse Source

添加服务端物流信息查询,添加小程序物流查询

Menethil 7 years ago
parent
commit
a64988a8df

+ 161 - 0
litemall-core/src/main/java/org/linlinjava/litemall/core/express/ExpressService.java

@@ -0,0 +1,161 @@
+package org.linlinjava.litemall.core.express;
+
+import org.linlinjava.litemall.core.express.config.ExpressConfig;
+import org.linlinjava.litemall.core.util.TinymallRequestUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.security.MessageDigest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 快鸟物流查询服务
+ * <p>
+ * 3831775044640 韵达快递(YD)
+ */
+@Service
+public class ExpressService {
+    //请求url
+    private String ReqURL = "http://api.kdniao.cc/Ebusiness/EbusinessOrderHandle.aspx";
+
+    @Autowired
+    ExpressConfig config;
+
+    /**
+     * 获取物流供应商名
+     *
+     * @param vendooCode
+     * @return
+     */
+    public String getVendorName(String vendooCode) {
+        for (Map<String, String> item : config.getVendors()) {
+            if (item.get("code").equals(vendooCode))
+                return item.get("name");
+        }
+        return null;
+    }
+
+    /**
+     * Json方式 查询订单物流轨迹
+     *
+     * @throws Exception
+     */
+    public String getOrderTracesByJson(String expCode, String expNo) throws Exception {
+        String requestData = "{'OrderCode':'','ShipperCode':'" + expCode + "','LogisticCode':'" + expNo + "'}";
+
+        Map<String, String> params = new HashMap<String, String>();
+        params.put("RequestData", urlEncoder(requestData, "UTF-8"));
+        params.put("EBusinessID", config.getAppId());
+        params.put("RequestType", "1002");
+        String dataSign = encrypt(requestData, config.getAppKey(), "UTF-8");
+        params.put("DataSign", urlEncoder(dataSign, "UTF-8"));
+        params.put("DataType", "2");
+
+        String result = TinymallRequestUtil.sendPost(ReqURL, params);
+
+        //根据公司业务处理返回的信息......
+
+        return result;
+    }
+
+    /**
+     * MD5加密
+     *
+     * @param str     内容
+     * @param charset 编码方式
+     * @throws Exception
+     */
+    @SuppressWarnings("unused")
+    private String MD5(String str, String charset) throws Exception {
+        MessageDigest md = MessageDigest.getInstance("MD5");
+        md.update(str.getBytes(charset));
+        byte[] result = md.digest();
+        StringBuffer sb = new StringBuffer(32);
+        for (int i = 0; i < result.length; i++) {
+            int val = result[i] & 0xff;
+            if (val <= 0xf) {
+                sb.append("0");
+            }
+            sb.append(Integer.toHexString(val));
+        }
+        return sb.toString().toLowerCase();
+    }
+
+    /**
+     * base64编码
+     *
+     * @param str     内容
+     * @param charset 编码方式
+     * @throws UnsupportedEncodingException
+     */
+    private String base64(String str, String charset) throws UnsupportedEncodingException {
+        String encoded = base64Encode(str.getBytes(charset));
+        return encoded;
+    }
+
+    @SuppressWarnings("unused")
+    private String urlEncoder(String str, String charset) throws UnsupportedEncodingException {
+        String result = URLEncoder.encode(str, charset);
+        return result;
+    }
+
+    /**
+     * 电商Sign签名生成
+     *
+     * @param content  内容
+     * @param keyValue Appkey
+     * @param charset  编码方式
+     * @return DataSign签名
+     * @throws UnsupportedEncodingException ,Exception
+     */
+    @SuppressWarnings("unused")
+    private String encrypt(String content, String keyValue, String charset) throws UnsupportedEncodingException, Exception {
+        if (keyValue != null) {
+            return base64(MD5(content + keyValue, charset), charset);
+        }
+        return base64(MD5(content, charset), charset);
+    }
+
+    private static char[] base64EncodeChars = new char[]{
+            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+            'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+            'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+            'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+            'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+            'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+            'w', 'x', 'y', 'z', '0', '1', '2', '3',
+            '4', '5', '6', '7', '8', '9', '+', '/'};
+
+    private static String base64Encode(byte[] data) {
+        StringBuffer sb = new StringBuffer();
+        int len = data.length;
+        int i = 0;
+        int b1, b2, b3;
+        while (i < len) {
+            b1 = data[i++] & 0xff;
+            if (i == len) {
+                sb.append(base64EncodeChars[b1 >>> 2]);
+                sb.append(base64EncodeChars[(b1 & 0x3) << 4]);
+                sb.append("==");
+                break;
+            }
+            b2 = data[i++] & 0xff;
+            if (i == len) {
+                sb.append(base64EncodeChars[b1 >>> 2]);
+                sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
+                sb.append(base64EncodeChars[(b2 & 0x0f) << 2]);
+                sb.append("=");
+                break;
+            }
+            b3 = data[i++] & 0xff;
+            sb.append(base64EncodeChars[b1 >>> 2]);
+            sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
+            sb.append(base64EncodeChars[((b2 & 0x0f) << 2) | ((b3 & 0xc0) >>> 6)]);
+            sb.append(base64EncodeChars[b3 & 0x3f]);
+        }
+        return sb.toString();
+    }
+}

+ 46 - 0
litemall-core/src/main/java/org/linlinjava/litemall/core/express/config/ExpressConfig.java

@@ -0,0 +1,46 @@
+package org.linlinjava.litemall.core.express.config;
+
+import org.linlinjava.litemall.core.util.YmlPropertyFactory;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Component
+@Configuration
+@PropertySource(value = {"classpath:application.yaml"}, factory = YmlPropertyFactory.class)
+@ConfigurationProperties(prefix = "express")
+public class ExpressConfig {
+    private String appId;
+    private String appKey;
+
+    private List<Map<String, String>> vendors = new ArrayList<>();
+
+    public List<Map<String, String>> getVendors() {
+        return vendors;
+    }
+
+    public void setVendors(List<Map<String, String>> vendors) {
+        this.vendors = vendors;
+    }
+
+    public String getAppKey() {
+        return appKey;
+    }
+
+    public void setAppKey(String appKey) {
+        this.appKey = appKey;
+    }
+
+    public String getAppId() {
+        return appId;
+    }
+
+    public void setAppId(String appId) {
+        this.appId = appId;
+    }
+}

+ 79 - 0
litemall-core/src/main/java/org/linlinjava/litemall/core/express/dao/ExpressInfo.java

@@ -0,0 +1,79 @@
+/**
+ * Copyright 2018 bejson.com
+ */
+package org.linlinjava.litemall.core.express.dao;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+/**
+ * Auto-generated: 2018-07-19 22:27:22
+ *
+ * @author bejson.com (i@bejson.com)
+ * @website http://www.bejson.com/java2pojo/
+ */
+public class ExpressInfo {
+
+    @JsonProperty("LogisticCode")
+    private String LogisticCode;
+    @JsonProperty("ShipperCode")
+    private String ShipperCode;
+    @JsonProperty("Traces")
+    private List<Traces> Traces;
+    @JsonProperty("State")
+    private String State;
+    @JsonProperty("EBusinessID")
+    private String EBusinessID;
+    @JsonProperty("Success")
+    private boolean Success;
+
+    public void setLogisticCode(String LogisticCode) {
+        this.LogisticCode = LogisticCode;
+    }
+
+    public String getLogisticCode() {
+        return LogisticCode;
+    }
+
+    public void setShipperCode(String ShipperCode) {
+        this.ShipperCode = ShipperCode;
+    }
+
+    public String getShipperCode() {
+        return ShipperCode;
+    }
+
+    public void setTraces(List<Traces> Traces) {
+        this.Traces = Traces;
+    }
+
+    public List<Traces> getTraces() {
+        return Traces;
+    }
+
+    public void setState(String State) {
+        this.State = State;
+    }
+
+    public String getState() {
+        return State;
+    }
+
+    public void setEBusinessID(String EBusinessID) {
+        this.EBusinessID = EBusinessID;
+    }
+
+    public String getEBusinessID() {
+        return EBusinessID;
+    }
+
+    public void setSuccess(boolean Success) {
+        this.Success = Success;
+    }
+
+    public boolean getSuccess() {
+        return Success;
+    }
+
+}

+ 37 - 0
litemall-core/src/main/java/org/linlinjava/litemall/core/express/dao/Traces.java

@@ -0,0 +1,37 @@
+/**
+ * Copyright 2018 bejson.com
+ */
+package org.linlinjava.litemall.core.express.dao;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Auto-generated: 2018-07-19 22:27:22
+ *
+ * @author bejson.com (i@bejson.com)
+ * @website http://www.bejson.com/java2pojo/
+ */
+public class Traces {
+
+    @JsonProperty("AcceptStation")
+    private String AcceptStation;
+    @JsonProperty("AcceptTime")
+    private String AcceptTime;
+
+    public void setAcceptStation(String AcceptStation) {
+        this.AcceptStation = AcceptStation;
+    }
+
+    public String getAcceptStation() {
+        return AcceptStation;
+    }
+
+    public void setAcceptTime(String AcceptTime) {
+        this.AcceptTime = AcceptTime;
+    }
+
+    public String getAcceptTime() {
+        return AcceptTime;
+    }
+
+}

+ 88 - 0
litemall-core/src/main/java/org/linlinjava/litemall/core/util/TinymallRequestUtil.java

@@ -0,0 +1,88 @@
+package org.linlinjava.litemall.core.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Map;
+
+/**
+ * 向指定 URL 发送POST方法的请求
+ *
+ * @return 远程资源的响应结果
+ */
+public class TinymallRequestUtil {
+    /**
+     * 向指定 URL 发送POST方法的请求
+     *
+     * @param url    发送请求的 URL
+     * @param params 请求的参数集合
+     * @return 远程资源的响应结果
+     */
+    @SuppressWarnings("unused")
+    public static String sendPost(String url, Map<String, String> params) {
+        OutputStreamWriter out = null;
+        BufferedReader in = null;
+        StringBuilder result = new StringBuilder();
+        try {
+            URL realUrl = new URL(url);
+            HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
+            // 发送POST请求必须设置如下两行
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+            // POST方法
+            conn.setRequestMethod("POST");
+            // 设置通用的请求属性
+            conn.setRequestProperty("accept", "*/*");
+            conn.setRequestProperty("connection", "Keep-Alive");
+            conn.setRequestProperty("user-agent",
+                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+            conn.connect();
+            // 获取URLConnection对象对应的输出流
+            out = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
+            // 发送请求参数
+            if (params != null) {
+                StringBuilder param = new StringBuilder();
+                for (Map.Entry<String, String> entry : params.entrySet()) {
+                    if (param.length() > 0) {
+                        param.append("&");
+                    }
+                    param.append(entry.getKey());
+                    param.append("=");
+                    param.append(entry.getValue());
+                    //System.out.println(entry.getKey()+":"+entry.getValue());
+                }
+                //System.out.println("param:"+param.toString());
+                out.write(param.toString());
+            }
+            // flush输出流的缓冲
+            out.flush();
+            // 定义BufferedReader输入流来读取URL的响应
+            in = new BufferedReader(
+                    new InputStreamReader(conn.getInputStream(), "UTF-8"));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result.append(line);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        //使用finally块来关闭输出流、输入流
+        finally {
+            try {
+                if (out != null) {
+                    out.close();
+                }
+                if (in != null) {
+                    in.close();
+                }
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+        return result.toString();
+    }
+}

+ 28 - 0
litemall-core/src/main/java/org/linlinjava/litemall/core/util/YmlPropertyFactory.java

@@ -0,0 +1,28 @@
+package org.linlinjava.litemall.core.util;
+
+import org.springframework.boot.env.PropertySourcesLoader;
+import org.springframework.core.env.PropertySource;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.EncodedResource;
+import org.springframework.core.io.support.PropertySourceFactory;
+
+import java.io.IOException;
+
+/**
+ * 某些情况下外部依赖导致 yaml 配置未能正确注入时,@PropertySource(value = {"classpath:application-wx.yaml"}, factory = YmlPropertyFactory.class) 手动注入配置文件
+ */
+public class YmlPropertyFactory implements PropertySourceFactory {
+    @Override
+    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
+        return name != null ? new PropertySourcesLoader().load(resource.getResource(), name, null) : new PropertySourcesLoader().load(
+                resource.getResource(), getNameForResource(resource.getResource()), null);
+    }
+
+    private static String getNameForResource(Resource resource) {
+        String name = resource.getDescription();
+        if (!org.springframework.util.StringUtils.hasText(name)) {
+            name = resource.getClass().getSimpleName() + "@" + System.identityHashCode(resource);
+        }
+        return name;
+    }
+}

+ 33 - 1
litemall-core/src/main/resources/application.yaml

@@ -38,4 +38,36 @@ WXNotifyConfig:
 NotifyPoolConfig:
   corePoolSize: 5
   maxPoolSize: 100
-  queueCapacity: 50
+  queueCapacity: 50
+
+#快鸟物流查询配置
+express:
+  appId: "XXXXXXXXX"
+  appKey: "XXXXXXXXXXXXXXXXXXXXXXXXX"
+  vendors:
+    - code: "ZTO"
+      name: "中通快递"
+    - code: "YTO"
+      name: "圆通速递"
+    - code: "YD"
+      name: "韵达速递"
+    - code: "YZPY"
+      name: "邮政快递包裹"
+    - code: "EMS"
+      name: "EMS"
+    - code: "DBL"
+      name: "德邦快递"
+    - code: "FAST"
+      name: "快捷快递"
+    - code: "ZJS"
+      name: "宅急送"
+    - code: "TNT"
+      name: "TNT快递"
+    - code: "UPS"
+      name: "UPS"
+    - code: "DHL"
+      name: "DHL"
+    - code: "FEDEX"
+      name: "FEDEX联邦(国内件)"
+    - code: "FEDEX_GJ"
+      name: "FEDEX联邦(国际件)"

+ 57 - 0
litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/web/WxExpressController.java

@@ -0,0 +1,57 @@
+package org.linlinjava.litemall.wx.web;
+
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.linlinjava.litemall.core.express.ExpressService;
+import org.linlinjava.litemall.core.express.dao.ExpressInfo;
+import org.linlinjava.litemall.core.util.JacksonUtil;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.wx.annotation.LoginUser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 物流信息查询接口
+ */
+@RestController
+@RequestMapping("/wx/express")
+public class WxExpressController {
+
+    @Autowired
+    ExpressService expressService;
+
+    @PostMapping("query")
+    public Object query(@LoginUser Integer userId, @RequestBody String body) {
+        if (userId == null) {
+            return ResponseUtil.unlogin();
+        }
+
+        String expCode = JacksonUtil.parseString(body, "expCode");
+        String expNo = JacksonUtil.parseString(body, "expNo");
+
+        try {
+            String result = expressService.getOrderTracesByJson(expCode, expNo);
+
+            ObjectMapper objMap = new ObjectMapper();
+            ExpressInfo ei = objMap.readValue(result, ExpressInfo.class);
+
+            Map<String, Object> data = new HashMap<>();
+            data.put("expCode", ei.getLogisticCode());
+            data.put("expNo", ei.getShipperCode());
+            data.put("expName", expressService.getVendorName(expCode));
+            data.put("Traces", ei.getTraces());
+
+            return ResponseUtil.ok(data);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return ResponseUtil.badArgument();
+    }
+}

+ 31 - 0
litemall-wx-api/src/test/java/org/linlinjava/litemall/wx/ExpressTest.java

@@ -0,0 +1,31 @@
+package org.linlinjava.litemall.wx;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.linlinjava.litemall.core.express.ExpressService;
+import org.linlinjava.litemall.core.express.dao.ExpressInfo;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+@WebAppConfiguration
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = Application.class)
+public class ExpressTest {
+
+    @Test
+    public void test() {
+        ExpressService expressService = new ExpressService();
+        String result = null;
+        try {
+            result = expressService.getOrderTracesByJson("YTO", "800669400640887922");
+            ObjectMapper objMap = new ObjectMapper();
+            ExpressInfo ei = objMap.readValue(result, ExpressInfo.class);
+            ei.getTraces();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        System.out.print(result);
+    }
+}

+ 3 - 1
litemall-wx/config/api.js

@@ -69,7 +69,9 @@ module.exports = {
     AddressSave: WxApiRoot + 'address/save',  //保存收货地址
     AddressDelete: WxApiRoot + 'address/delete',  //保存收货地址
 
-    RegionList: WxApiRoot + 'region/list',  //获取区域列表
+  ExpressQuery: WxApiRoot + 'express/query',//物流查询
+
+  RegionList: WxApiRoot + 'region/list', //获取区域列表
 
     OrderSubmit: WxApiRoot + 'order/submit', // 提交订单
     OrderPrepay: WxApiRoot + 'order/prepay', // 订单的预支付会话

+ 28 - 0
litemall-wx/pages/ucenter/orderDetail/orderDetail.js

@@ -6,6 +6,8 @@ Page({
     orderId: 0,
     orderInfo: {},
     orderGoods: [],
+    expressInfo: {},
+    flag: false,
     handleOption: {}
   },
   onLoad: function (options) {
@@ -15,6 +17,27 @@ Page({
     });
     this.getOrderDetail();
   },
+  getOrderExpress: function() {
+    let that = this;
+    util.request(api.ExpressQuery, {
+      expCode: that.data.orderInfo.expCode,
+      expNo: that.data.orderInfo.expNo
+    }, 'POST').then(function(res) {
+      if (res.errno === 0) {
+        that.setData({
+          expressInfo: res.data
+        });
+
+        console.log(that.data.expressInfo);
+      }
+    });
+  },
+  expandDetail: function() {
+    let that = this;
+    this.setData({
+      flag: !that.data.flag
+    })
+  },
   getOrderDetail: function () {
     let that = this;
     util.request(api.OrderDetail, {
@@ -27,6 +50,11 @@ Page({
           orderGoods: res.data.orderGoods,
           handleOption: res.data.orderInfo.handleOption
         });
+
+        // 请求物流信息,仅当订单状态为发货时才请求
+        if (that.data.handleOption.confirm) {
+          that.getOrderExpress();
+        }
       }
     });
   },

+ 21 - 0
litemall-wx/pages/ucenter/orderDetail/orderDetail.wxml

@@ -40,6 +40,27 @@
                 </view>
             </view>
         </view>
+    <!-- 物流信息,仅收货状态下可见 -->
+    <view class="order-express" bindtap="expandDetail" wx:if="{{ handleOption.confirm }}">
+      <view class="order-express">
+        <view class="title">
+          <view class="t">快递公司:{{expressInfo.expNo}}</view>
+          <view class="b">物流单号:{{expressInfo.expCode}}</view>
+        </view>
+      </view>
+    </view>
+
+    <!-- 展开 -->
+    <view class="data-expand p10 border-bottom" wx:if="{{ flag }}">
+      <view class="order-express">
+        <view class="traces" wx:for="{{expressInfo.Traces}}" wx:key="item" wx:for-item="iitem">
+          <view class="trace">
+            <view class="acceptTime">{{iitem.AcceptTime}}</view>
+            <view class="acceptStation">{{iitem.AcceptStation}}</view>
+          </view>
+        </view>
+      </view>
+    </view>
     </view>
 
     <view class="order-bottom">

+ 62 - 1
litemall-wx/pages/ucenter/orderDetail/orderDetail.wxss

@@ -270,4 +270,65 @@ page{
     display: inline-block;
     width: 140rpx;
     color: #b4282d;
-}
+}
+
+.order-express {
+  margin-top: 20rpx;
+  padding-left: 31.25rpx;
+  height: auto;
+}
+
+.order-express .title {
+  width: 750rpx;
+  height: 108rpx;
+  background: #fff;
+  margin-bottom: 20rpx;
+}
+
+.order-express .title.t {
+  float: left;
+  width: 600rpx;
+  height: 108rpx;
+  line-height: 108rpx;
+  font-size: 29rpx;
+  margin-left: 31.25rpx;
+
+}
+
+.order-express .title.b {
+  float: left;
+  width: 600rpx;
+  height: 108rpx;
+  line-height: 108rpx;
+  font-size: 29rpx;
+  margin-left: 31.25rpx;
+}
+
+.order-express .traces {
+  height: auto;
+  padding-bottom: 17.5rpx;
+  padding-top: 17.5rpx;
+  border-bottom: 1px solid #f4f4f4;
+}
+
+.order-express .traces.trace {
+  height: 180rpx;
+  padding-bottom: 17.5rpx;
+  padding-top: 17.5rpx;
+  border-bottom: 1px solid #f4f4f4;
+}
+
+.order-express .traces.trace.acceptTime {
+  display: inline-block;
+  height: 35rpx;
+  width: 140rpx;
+  line-height: 35rpx;
+  font-size: 24rpx;
+}
+
+.order-express .traces.trace.acceptStation {
+  display: inline-block;
+  height: 35rpx;
+  line-height: 35rpx;
+  font-size: 24rpx;
+}