手擼的一個快遞查詢系統,竟然閱讀量過1.8w


手擼的一個快遞查詢系統,竟然閱讀量過1.8w

一、目的

做這個項目的初衷是因為我去年在微信賣老家水果,好多朋友下單後都問我快遞單號,每天發貨後我都要挨個甄別這個人是哪個快遞信息,很麻煩一部小心就搞錯了。基於這件小事我有了自助快遞查詢的這個想法。將發貨的快遞信息導入到我的系統裡,用戶訪問我的系統,通過輸入手機號就可以查看自己的快遞物流信息。 項目是去年8月寫的,一直擱淺在哪,最近無意間翻看我發的那篇文章自助快遞單號查詢閱讀量竟然都1.8w了,有圖有真相。

手擼的一個快遞查詢系統,竟然閱讀量過1.8w

這著實讓我很震驚,看來自助快遞查詢這塊確實是個熱點。今天我就講一下我手擼的快遞查詢系統。

二、開發

項目地址:github.com/hellowHuaai… 有興趣的可以直接下載源碼,覺得項目不錯的夥伴記得點個star,謝謝啦!

2.1技術棧

項目涉及到的技術棧有:

  • SpringBoot: 一款 Java 微服務框架。Spring boot 是 Spring 家族中的一個新框架,它用來簡化 Spring 應用程序的創建和開發。
  • Mybitas: 一款ORM框架,即對象關係映射。ORM框架的作用是把持久化對象的保存、修改、刪除等操作,轉換成對數據庫的操作。
  • Jquery:一個輕量級的寫的少,做的多的 JavaScript 函數庫。
  • Bootstrap:Bootstrap 是一個用於快速開發 Web 應用程序和網站的前端框架。Bootstrap 是基於 HTML、CSS、JAVASCRIPT 的。

2.2後端開發

創建entity 創建快遞單實體類,屬性包括id,用戶名(userName),電話(phone),快遞單號(kuaidiNo),快遞公司(company),數據創建時間(createTime)。代碼如下:

<code>@Data
@Builder
public class KuaiDi {
private Integer id;
/* 收件人姓名 */
private String userName;
/**收件人電話*/
private String phone;
/* 快遞單號*/
private String kuaidiNo;
/*快遞公司名稱(拼音)*/
private String company;
/*訂單創建時間*/
private Date createTime;

public KuaiDi(Integer id, String userName, String phone, String kuaidiNo, String company, Date createTime) {
this.id = id;
this.userName = userName;
this.phone = phone;
this.kuaidiNo = kuaidiNo;
this.company = company;
this.createTime = createTime;
}
public KuaiDi(Integer id, String userName, String phone, String kuaidiNo, String company) {
this.id = id;
this.userName = userName;
this.phone = phone;
this.kuaidiNo = kuaidiNo;
this.company = company;
}
}/<code>

service,mapper是常規的增刪查改操作,就是保存快遞的基本信息在數據庫中,並可以對數據進行簡單的維護功能。詳細可參考項目源碼。接下來看核心代碼。

查詢快遞信息 快遞的基本信息存入數據庫,然後就是通過這些信息查詢快遞的詳細物流信息。這裡我做過很多嘗試,想直接調用一些快遞公司的快遞信息查詢接口,但是都發現接口都有session,當session失效後就無法查詢到數據或者就查詢到的數據不正確。最終在網上找到一種付費的方案,使用快遞100接口。www.kuaidi100.com/ 查詢快遞的demo代碼如下:

<code>public class SynQueryDemo {

\t/**
\t * 實時查詢請求地址
\t */
\tprivate static final String SYNQUERY_URL = "http://poll.kuaidi100.com/poll/query.do";
\t
\tprivate String key;\t\t\t//授權key
\tprivate String customer;\t//實時查詢公司編號

\tpublic SynQueryDemo(String key, String customer) {
\t\tthis.key = key;
\t\tthis.customer = customer;
\t}
\t
\t/**
\t * 實時查詢快遞單號
\t * @param com\t\t\t快遞公司編碼
\t * @param num\t\t\t快遞單號
\t * @param phone\t\t\t手機號
\t * @param from\t\t\t出發地城市
\t * @param to\t\t\t目的地城市
\t * @param resultv2\t\t開通區域解析功能:0-關閉;1-開通
\t * @return
\t */
\tpublic String synQueryData(String com, String num, String phone, String from, String to, int resultv2) {

\t\tStringBuilder param = new StringBuilder("{");
\t\tparam.append("\"com\":\"").append(com).append("\"");
\t\tparam.append(",\"num\":\"").append(num).append("\"");
\t\tparam.append(",\"phone\":\"").append(phone).append("\"");
\t\tparam.append(",\"from\":\"").append(from).append("\"");
\t\tparam.append(",\"to\":\"").append(to).append("\"");
\t\tif(1 == resultv2) {
\t\t\tparam.append(",\"resultv2\":1");
\t\t} else {
\t\t\tparam.append(",\"resultv2\":0");
\t\t}
\t\tparam.append("}");
\t\t
\t\tMap<string> params = new HashMap<string>();
\t\tparams.put("customer", this.customer);
\t\tString sign = MD5Utils.encode(param + this.key + this.customer);

\t\tparams.put("sign", sign);
\t\tparams.put("param", param.toString());
\t\t
\t\treturn this.post(params);
\t}
\t
\t/**
\t * 發送post請求
\t */
\tpublic String post(Map<string> params) {
\t\tStringBuffer response = new StringBuffer("");
\t\t
\t\tBufferedReader reader = null;
\t\ttry {
\t\t\tStringBuilder builder = new StringBuilder();
\t\t\tfor (Map.Entry<string> param : params.entrySet()) {
\t\t\t\tif (builder.length() > 0) {
\t\t\t\t\tbuilder.append('&');
\t\t\t\t}
\t\t\t\tbuilder.append(URLEncoder.encode(param.getKey(), "UTF-8"));
\t\t\t\tbuilder.append('=');
\t\t\t\tbuilder.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));
\t\t\t}
\t\t\tbyte[] bytes = builder.toString().getBytes("UTF-8");

\t\t\tURL url = new URL(SYNQUERY_URL);
\t\t\tHttpURLConnection conn = (HttpURLConnection) url.openConnection();
\t\t\tconn.setConnectTimeout(3000);
\t\t\tconn.setReadTimeout(3000);
\t\t\tconn.setRequestMethod("POST");
\t\t\tconn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
\t\t\tconn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
\t\t\tconn.setRequestProperty("Content-Length", String.valueOf(bytes.length));
\t\t\tconn.setDoOutput(true);
\t\t\tconn.getOutputStream().write(bytes);

\t\t\treader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
\t\t\t
\t\t\tString line = "";
while ((line = reader.readLine()) != null) {
\tresponse.append(line);
}
\t\t} catch (Exception e) {
\t\t\te.printStackTrace();
\t\t} finally {
\t\t\ttry {
\t\t\t\tif (null != reader) {
\t\t\t\t\treader.close();
\t\t\t\t}

\t\t\t} catch (IOException e) {
\t\t\t\te.printStackTrace();
\t\t\t}
\t\t}
\t\t
\t\treturn response.toString();
\t}
}/<string>/<string>/<string>/<string>/<code>

上面的代碼就是通過java代碼調用kuaidi100的查詢接口,這個查詢接口會通過快遞單號自動識別快遞是屬於哪個快遞公司,然後調用對應快遞公司接口獲取響應數據。付費購買接口使用權其實就是生成一個授權key和實時查詢公司編號customer,在線調用會做身份認證。這樣就可以獲取快遞信息的json數據了。我已經購買了100塊大洋的接口使用權,大家可直接調用快遞查詢接口。

controller代碼 快遞信息增刪查改的controller就不在列了,這裡主要看下我對查詢快遞的接口進行了一次包裝處理。代碼如下:

<code>@RestController
public class KuaiDiQueryController {

@Autowired
private KuaiDiService kuaiDiService;
@Autowired
private KuaiDiQueryService kuaiDiQueryService;

/**
* 返回json數據
* @param com
* @param no
* @return

*/
@GetMapping("/getKuaiDiInfoByJson")
@ResponseBody
public String queryKuadiInfoByJson(String com, String no) {
return kuaiDiQueryService.synQueryData(com, no,"", "", "", 0);
}

@GetMapping("/getKuaiDiInfoByPhone")
@ResponseBody
public Response queryKuaidiByPhone(String phone){
Response response = new Response();
if(StringUtils.isNotEmpty(phone)){
List<responsedata> responseDataList = new ArrayList<>();
// 1.通過手機號查詢下面的所有訂單號
List<kuaidi> kuaiDiList = kuaiDiService.getList("", phone);
if(!CollectionUtils.isEmpty(kuaiDiList)){
kuaiDiList.forEach(kuaiDi -> {
// 2.依次查出所有的訂單號
String responseDataStr = kuaiDiQueryService.synQueryData(kuaiDi.getCompany(), kuaiDi.getKuaidiNo(),"", "", "", 0);
ResponseData responseData = CommonUtils.convertJsonStr2Object(responseDataStr);
responseDataList.add(responseData);
});
}
// 3.組裝數據返回給前臺
response.setDataList(responseDataList);
}
return response;
}
}/<kuaidi>/<responsedata>/<code>

2.3前端開發

前端展示主要包括兩個頁面,管理員頁面和客戶頁面。管理員頁面功能包括快遞信息的新增,修改,刪除,分頁查詢,在線快遞物流信息接口。客戶頁面包括快遞信息的分頁查詢和在線快遞物流信息接口。所以主要看一下管理員頁面。

html頁面

html頁面引入了jQuery和Bootstrap,jQuery已經過時了,但是使用起來還是很方便的。

<code>

<title>快遞單號查詢/<title>

<link>


<link>


...









快遞單號自助查詢






<label>收件人姓名:/<label>



<label>收件人電話:/<label>


<button>查詢/<button>
<button>重置/<button>
<button>新增/<button>




<table>






/<code>

admin.js 這裡說明一下前端我引入的jQuery,包括新增,修改,刪除,查詢的功能,查詢事件添加了對電話號碼的必填校驗。

<code>var $testTable = $('#testTable');
$testTable.bootstrapTable({
url: 'getList',
queryParams: function (params) {
return {
offset: params.offset,
limit: params.limit,
userName: $('#queryNameText').val(),
phone: $('#queryPhoneText').val()
}
},
columns: [{
field: 'id',
title: '編號'
}, {
field: 'userName',
title: '收件人姓名'
}, {
field: 'phone',
title: '收件人電話'
}, {
field: 'company',
title: '快遞公司'
},{
field: 'kuaidiNo',
title: '快遞單號',
formatter: function (value, row, index) {
return [
\t\t\t\t' ',
].join('');
},
}, {
formatter: function (value, row, index) {
return [
' ',
' '
].join('');
},

title: '操作'
}],
striped: true,
pagination: true,
sidePagination: 'server',
pageSize: 10,
pageList: [5, 10, 25, 50, 100],
rowStyle: function (row, index) {
var ageClass = '';
if (row.age < 18) {
ageClass = 'text-danger';
}
return {classes: ageClass}
},
});

// 設置bootbox中文
bootbox.setLocale('zh_CN');

var titleTip = '提示';

function kuaidiRecordInfo(no, company) {
$('#viewModal').modal('show');
$.ajax({
type:'get',
url:'getKuaiDiInfoByJson?com='+ company +'&no=' + no,
cache:false,
dataType:'json',
success:function(result){
// 顯示詳細信息 發送請求通過單號
\t\t\t$("#viewDataList").empty();
console.log(result.data);
var dataList = result.data;
if(null != dataList){
\t\t\t\t$("#viewDataList").append('
  • ');
    \t\t\t\t$("#kuaidi").append('
    <time>時間/<time>地點和跟蹤進度
      /');
      for(var i=0;i<datalist.length> var kuaiRecodList = dataList[i];
      if( i == 0){
      $("#reordList").append('
    • '+ kuaiRecodList.ftime + '
      '+ kuaiRecodList.context +'
    • ');
      }else{
      $("#reordList").append('
    • '+ kuaiRecodList.ftime + '
      '+ kuaiRecodList.context +'
    • ');
      }
      }
      }
      }
      });
      }

      // 驗證姓名和地址是否為空
      function verifyNameAndAddress(name, address) {
      if (name != '' && address != '') {
      return true;
      }
      return false;
      }

      function nullAlert() {
      bootbox.alert({
      title: titleTip,
      message: '所有項均為必填!'
      });
      }

      // 點擊查詢按鈕
      $('#queryBtn').click(function () {
      var age = $('#queryAgeText').val();
      // 刷新並跳轉到第一頁
      $testTable.bootstrapTable('selectPage', 1);

      });

      // 點擊重置按鈕,清空查詢條件並跳轉回第一頁

      $('#resetBtn').click(function() {
      $('.form-group :text').val('');
      $testTable.bootstrapTable('selectPage', 1);
      });

      // 用於修改服務器資源
      function exchangeData(path, id, userName, phone, kuaiDiNo, company) {
      $.ajax({
      url: path,
      type: 'post',
      data : {
      id: id,
      userName: userName,
      phone: phone,
      kuaiDiNo: kuaiDiNo,
      company: company
      },
      success: function(res) {
      bootbox.alert({
      title: titleTip,
      message: res.message
      });
      // 在每次提交操作後返回首頁
      $testTable.bootstrapTable('selectPage', 1);
      }
      });
      }

      // 新增
      $('#addBtn').click(function() {
      $('#addNameText').val('');
      $('#addPhoneText').val('');
      $('#addKuaiDiNoText').val('');
      \t $('#addCompanyText').val('');
      $('#addModal').modal('show');
      });

      $('#saveAdd').click(function() {
      $('#addModal').modal('hide');
      bootbox.confirm({
      title: titleTip,
      message: '確認增加?',
      callback: function (flag) {
      if (flag) {
      var userName = $('#addNameText').val();
      var phone = $('#addPhoneText').val();
      var kuaiDiNo = $('#addKuaiDiNoText').val();
      var company = $('#addCompanyText').val();

      if (verifyNameAndAddress(userName, kuaiDiNo)) {
      exchangeData('addKuaiDi', null, userName, phone, kuaiDiNo, company);
      } else {
      nullAlert();
      }
      }
      }
      });
      });

      var mid;

      // 修改
      function modifyKuaiDi(id, name, age, address) {
      mid = id;
      $('#modifyNameText').val(name);
      $('#modifyPhoneText').val(age);
      $('#modifyKuaiDiNoText').val(address);
      \t$('#modifyCompanyText').val(address);
      $('#modifyModal').modal('show');
      }

      $('#saveModify').click(function() {
      $('#modifyModal').modal('hide');
      bootbox.confirm({
      title: titleTip,
      message: '確認修改?',
      callback: function (flag) {
      if (flag) {
      var userName = $('#modifyNameText').val();
      var phone = $('#modifyPhoneText').val();
      var kuaiDiNo = $('#modifyKuaiDiNoText').val();
      var company = $('#modifyCompanyText').val();
      if (verifyNameAndAddress(userName, phone)) {
      exchangeData('modifyKuaiDi', mid, userName, phone, kuaiDiNo, company);
      } else {
      nullAlert();
      }
      }
      }
      });
      });

      // 刪除
      function delKuaiDi(id) {
      bootbox.confirm({
      title: titleTip,
      message: '確認刪除?',
      callback: function(flag) {

      if (flag) {
      exchangeData("delKuaiDi", id);
      }
      }
      });
      /<datalist.length>/<code>

    2.4運行項目

    修改配置文件 項目配置文件src/resources/application.properties,根據實際情況修改對應的數據庫連接信息。

    <code>#MySQL配置
    spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    spring.datasource.url=jdbc:mysql://localhost:3306/kuaidi?useUnicode=true&characterEncoding=UTF-8
    spring.datasource.username=root #數據庫賬號
    spring.datasource.password=root #數據庫密碼
    #MyBatis日誌配置
    mybatis.mapperLocations=classpath:mapper/*.xml
    mybatis.config-location=classpath:/config/mybatis-config.xml
    #端口配置
    server.port=8082

    # 定位模板的目錄
    spring.mvc.view.prefix=classpath:/templates/
    # 給返回的頁面添加後綴名
    spring.mvc.view.suffix=.html/<code>

    創建數據庫表 表結構如下:

    <code>DROP TABLE IF EXISTS `kuaidi`;
    CREATE TABLE `kuaidi` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '收件人姓名',
    `phone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '收件人電話',
    `kuaidi_no` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '快遞單號',
    `company` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '快遞公司名稱拼音',

    `create_time` datetime(0) NULL DEFAULT NULL,
    PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;/<code>

    運行 將項目導入Idea工具,找到com.wangzg.kuaidi.KuaiDiApplication文件,執行main方法即可,如下圖:

    手擼的一個快遞查詢系統,竟然閱讀量過1.8w

    三、部署

    3.1 jar部署

    上傳安裝包 在服務器創建/usr/myworkspace,執行下面命令可直接創建:

    <code>mkdir -p /usr/myworkspace
    複製代碼/<code>

    下載相關文件,上傳到服務器/usr/myworkspace。下載地址:github.com/hellowHuaai… 文件主要包括:

    • application.properties 說明:項目配置文件,可能會涉及到修改服務器端口,數據庫訪問、端口、賬號、密碼等。
    • kuaidi.jar 說明:後端服務的可執行jar文件。
    • kuaidi.sql 說明:數據庫初始化腳本。
    • start.sh 說明: 啟動服務器shell腳本。
    • stop.sh 說明: 停止服務器shell腳本。

    初始化數據庫 打開Navicat工具,選中數據庫,右鍵選擇運行SQL文件...,具體操作,這樣數據庫就初始化完成。

    手擼的一個快遞查詢系統,竟然閱讀量過1.8w

    運行項目
    在服務器/usr/myworkspace目錄下,執行如下命令,即可運行項目:

    <code>chmod +x *.sh #給所有 .sh文件添加執行權限
    ./start.sh/<code>

    3.2 Docker部署

    Docker 容器化部署項目,需要創建一個 mysql 的容器,創建kuaidi的容器,再初始化一下數據庫。

    創建數據庫容器 代碼如下:

    <code>docker run -d --name mysql5.7 -e MYSQL_ROOT_PASSWORD=root -it -p 3306:3306 daocloud.io/library/mysql:5.7.7-rc/<code>

    導入數據庫腳本 數據庫腳本kuaidi.sql內容如下:

    <code>create DATABASE kuaidi;
    use kuaidi;

    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;

    DROP TABLE IF EXISTS `kuaidi`;
    CREATE TABLE `kuaidi` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '收件人姓名',
    `phone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '收件人電話',
    `kuaidi_no` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '快遞單號',
    `company` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '快遞公司名稱拼音',
    `create_time` datetime(0) NULL DEFAULT NULL,

    PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;/<code>

    然後執行下面命令,就可以導入kuaidi.sql腳本:

    <code>docker exec -i mysql5.7 mysql -uroot -proot mysql < kuaidi.sql/<code>

    創建kuaidi容器 執行下面命令就可以創建容器:

    <code>docker run -d -p 9082:8082 -v application.properties:/home/conf/application.properties --name kuaidi1 huaairen/kuaidi:latest/<code>

    注:application.properties文件為項目的配置文件,在src/main/resources目錄下;huaairen/kuaidi:latest是我打包好的鏡像,直接下載就可以。

    四、最後

    項目功能還特別簡陋,很多功能需要開發和完善。如果你也遇到類似的問題我們可以一起討論,合作共贏哦!

    鏈接:https://juejin.im/post/5e9313ece51d4546c62f9ac4


    分享到:


    相關文章: