龙空技术网

手撸的一个快递查询系统,竟然阅读量过1.8w

代码小当家 3204

前言:

如今大家对“cdnbootcss”大致比较关切,同学们都需要分析一些“cdnbootcss”的相关资讯。那么小编同时在网上汇集了一些有关“cdnbootcss””的相关资讯,希望大家能喜欢,看官们一起来了解一下吧!

一、目的

做这个项目的初衷是因为我去年在微信卖老家水果,好多朋友下单后都问我快递单号,每天发货后我都要挨个甄别这个人是哪个快递信息,很麻烦一部小心就搞错了。基于这件小事我有了自助快递查询的这个想法。将发货的快递信息导入到我的系统里,用户访问我的系统,通过输入手机号就可以查看自己的快递物流信息。 项目是去年8月写的,一直搁浅在哪,最近无意间翻看我发的那篇文章自助快递单号查询阅读量竟然都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)。代码如下:

@Data@Builderpublic 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;    }}

service,mapper是常规的增删查改操作,就是保存快递的基本信息在数据库中,并可以对数据进行简单的维护功能。详细可参考项目源码。接下来看核心代码。

查询快递信息 快递的基本信息存入数据库,然后就是通过这些信息查询快递的详细物流信息。这里我做过很多尝试,想直接调用一些快递公司的快递信息查询接口,但是都发现接口都有session,当session失效后就无法查询到数据或者就查询到的数据不正确。最终在网上找到一种付费的方案,使用快递100接口。 查询快递的demo代码如下:

public class SynQueryDemo {	/**	 * 实时查询请求地址	 */	private static final String SYNQUERY_URL = ";;		private String key;			//授权key	private String customer;	//实时查询公司编号	public SynQueryDemo(String key, String customer) {		this.key = key;		this.customer = customer;	}		/**	 * 实时查询快递单号	 * @param com			快递公司编码	 * @param num			快递单号	 * @param phone			手机号	 * @param from			出发地城市	 * @param to			目的地城市	 * @param resultv2		开通区域解析功能:0-关闭;1-开通	 * @return	 */	public String synQueryData(String com, String num, String phone, String from, String to, int resultv2) {		StringBuilder param = new StringBuilder("{");		param.append("\"com\":\"").append(com).append("\"");		param.append(",\"num\":\"").append(num).append("\"");		param.append(",\"phone\":\"").append(phone).append("\"");		param.append(",\"from\":\"").append(from).append("\"");		param.append(",\"to\":\"").append(to).append("\"");		if(1 == resultv2) {			param.append(",\"resultv2\":1");		} else {			param.append(",\"resultv2\":0");		}		param.append("}");				Map<String, String> params = new HashMap<String, String>();		params.put("customer", this.customer);		String sign = MD5Utils.encode(param + this.key + this.customer);		params.put("sign", sign);		params.put("param", param.toString());				return this.post(params);	}		/**	 * 发送post请求	 */	public String post(Map<String, String> params) {		StringBuffer response = new StringBuffer("");				BufferedReader reader = null;		try {			StringBuilder builder = new StringBuilder();			for (Map.Entry<String, String> param : params.entrySet()) {				if (builder.length() > 0) {					builder.append('&');				}				builder.append(URLEncoder.encode(param.getKey(), "UTF-8"));				builder.append('=');				builder.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));			}			byte[] bytes = builder.toString().getBytes("UTF-8");			URL url = new URL(SYNQUERY_URL);			HttpURLConnection conn = (HttpURLConnection) url.openConnection();			conn.setConnectTimeout(3000);			conn.setReadTimeout(3000);			conn.setRequestMethod("POST");			conn.setRequestProperty("accept", "*/*");            conn.setRequestProperty("connection", "Keep-Alive");			conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");			conn.setRequestProperty("Content-Length", String.valueOf(bytes.length));			conn.setDoOutput(true);			conn.getOutputStream().write(bytes);			reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));						String line = "";            while ((line = reader.readLine()) != null) {            	response.append(line);            }		} catch (Exception e) {			e.printStackTrace();		} finally {			try {				if (null != reader) {					reader.close();				}			} catch (IOException e) {				e.printStackTrace();			}		}				return response.toString();	}}

上面的代码就是通过java代码调用kuaidi100的查询接口,这个查询接口会通过快递单号自动识别快递是属于哪个快递公司,然后调用对应快递公司接口获取响应数据。付费购买接口使用权其实就是生成一个授权key和实时查询公司编号customer,在线调用会做身份认证。这样就可以获取快递信息的json数据了。我已经购买了100块大洋的接口使用权,大家可直接调用快递查询接口。

controller代码 快递信息增删查改的controller就不在列了,这里主要看下我对查询快递的接口进行了一次包装处理。代码如下:

@RestControllerpublic 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;    }}
2.3前端开发

前端展示主要包括两个页面,管理员页面和客户页面。管理员页面功能包括快递信息的新增,修改,删除,分页查询,在线快递物流信息接口。客户页面包括快递信息的分页查询和在线快递物流信息接口。所以主要看一下管理员页面。

html页面 html页面引入了jQuery和Bootstrap,jQuery已经过时了,但是使用起来还是很方便的。

<html><head>  <title>快递单号查询</title>    <script src=";></script>    <link href="; rel="stylesheet">    <script src=";></script>    <script src=";></script>    <link href="; rel="stylesheet">    <script src=";></script>    <script src=";></script>  ...</head><body>    <div class="container-fluid">        <div class="row">            <nav class="navbar navbar-inverse navbar-fixed-top">                <a class="navbar-brand" href=";>我的个人主页</a>                <button class="navbar-toggle" data-toggle="collapse" data-target="#collapseMenu">                    <span class="icon-bar"></span>                    <span class="icon-bar"></span>                    <span class="icon-bar"></span>                </button>                <div class="collapse navbar-collapse" id="collapseMenu">                    <ul class="nav navbar-nav">                        <li class="nav-li">                            <a href="; target="_blank">本项目github</a>                        </li>                    </ul>                </div>            </nav>        </div>        <h1 class="page-header">            快递单号自助查询        </h1>        <!-- 查询工具栏 -->        <div class="form-inline">            <div class="form-group">                <label for="queryNameText">收件人姓名:</label>                <input id="queryNameText" class="form-control input-sm">            </div>            <div class="form-group">                <label for="queryPhoneText">收件人电话:</label>                <input id="queryPhoneText" class="form-control input-sm">            </div>            <button class="btn btn-primary btn-sm" id="queryBtn">查询</button>            <button class="btn btn-primary btn-sm" id="resetBtn">重置</button>            <button class="btn btn-primary btn-sm" id="addBtn">新增</button>        </div>        <hr>        <table id="testTable"></table>        <!-- 查看订单信息模态窗 -->        <div class="modal fade" id="viewModal">            <div class="modal-dialog">                <div class="modal-content">                    <div class="modal-header">                        <button type="button" class="close" data-dismiss="modal">×</button>                        <h4 class="modal-title">订单信息</h4>                    </div>                    <div class="modal-body" id="viewDataList"></div>                    <div class="modal-footer">                        <button class="btn btn-default" data-dismiss="modal">关闭</button>                    </div>                </div>            </div>        </div>        <!-- 新增模态窗 -->        <div class="modal fade" id="addModal">            <div class="modal-dialog">                <div class="modal-content">                    <div class="modal-header">                        <button type="button" class="close" data-dismiss="modal">×</button>                        <h4 class="modal-title">新增信息</h4>                    </div>                    <div class="modal-body">                        <div class="form-inline">                        </div>                        <div class="form-group">                            <label for="addNameText">收件人姓名:</label>                            <input id="addNameText" class="form-control input-sm">                        </div>                        <div class="form-group">                            <label for="addPhoneText">收件人电话:</label>                            <input id="addPhoneText" class="form-control input-sm">                        </div>                        <div class="form-group">                            <label for="addKuaiDiNoText">快递单号:</label>                            <input id="addKuaiDiNoText" class="form-control input-sm">                        </div>                        <div class="form-group">                            <label for="addCompanyText">快递公司(拼音):</label>                            <input id="addCompanyText" class="form-control input-sm">                        </div>                    </div>                    <div class="modal-footer">                        <button class="btn btn-default" data-dismiss="modal">关闭</button>                        <button class="btn btn-primary" id="saveAdd">保存</button>                    </div>                </div>            </div>        </div>        <!-- 修改模态窗 -->        <div class="modal fade" id="modifyModal">            <div class="modal-dialog">                <div class="modal-content">                    <div class="modal-header">                        <button type="button" class="close" data-dismiss="modal">×</button>                        <h4 class="modal-title">修改信息</h4>                    </div>                    <div class="modal-body">                        <div class="form-inline">                        </div>                        <div class="form-group">                            <label for="modifyNameText">收件人姓名:</label>                            <input id="modifyNameText" class="form-control input-sm">                        </div>                        <div class="form-group">                            <label for="modifyPhoneText">收件人电话:</label>                            <input id="modifyPhoneText" class="form-control input-sm">                        </div>                        <div class="form-group">                            <label for="modifyKuaiDiNoText">快递单号:</label>                            <input id="modifyKuaiDiNoText" class="form-control input-sm">                        </div>                        <div class="form-group">                            <label for="modifyCompanyText">快递公司(拼音):</label>                            <input id="modifyCompanyText" class="form-control input-sm">                        </div>                    </div>                    <div class="modal-footer">                        <button class="btn btn-default" data-dismiss="modal">关闭</button>                        <button class="btn btn-primary" id="saveModify">保存</button>                    </div>                </div>            </div>        </div>    </div>  <!-- container-fluid -->    <script type="text/javascript" src="js/admin.js"></script></body></html>

admin.js 这里说明一下前端我引入的jQuery,包括新增,修改,删除,查询的功能,查询事件添加了对电话号码的必填校验。

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 [				'<a onclick="kuaidiRecordInfo(' + "'" + row.kuaidiNo + "','" + row.company + "')" + '">' + row.kuaidiNo +'</a>',            ].join('');        },    }, {        formatter: function (value, row, index) {            return [                '<a href="javascript:modifyKuaiDi(' + row.id + ",'" + row.userName + "'," + row.phone + ",'" + row.kuaidiNo + "'" + ')">' +                    '<i class="glyphicon glyphicon-pencil"></i>修改' +                '</a>',                '<a href="javascript:delKuaiDi(' + row.id + ')">' +                    '<i class="glyphicon glyphicon-remove"></i>删除' +                '</a>'            ].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){            // 显示详细信息 发送请求通过单号			$("#viewDataList").empty();            console.log(result.data);            var dataList = result.data;            if(null != dataList){				$("#viewDataList").append('<li class="accordion-navigation"><a href="#kuaidi'+ '">快递单号:'+ result.nu +'</a><div id="kuaidi'+ '" class="content"></div></li>');				$("#kuaidi").append('<section class="result-box"><div id="resultTop" class="flex result-top"><time class="up">时间</time><span>地点和跟踪进度</span></div><ul id="reordList'+'" class="result-list sortup"></ul></section>');                  for(var i=0;i<dataList.length; i++){                    var kuaiRecodList = dataList[i];                    if( i == 0){                        $("#reordList").append('<li class="last finish"><div class="time"> '+ kuaiRecodList.ftime + '</div><div class="dot"></div><div class="text"> '+ kuaiRecodList.context +'</div></li>');                    }else{                        $("#reordList").append('<li class=""><div class="time"> '+ kuaiRecodList.ftime + '</div><div class="dot"></div><div class="text"> '+ kuaiRecodList.context +'</div></li>');                    }                }            }        }    });}// 验证姓名和地址是否为空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('');	   $('#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);	$('#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);            }        }    });
2.4运行项目

修改配置文件 项目配置文件src/resources/application.properties,根据实际情况修改对应的数据库连接信息。

#MySQL配置spring.datasource.type=com.alibaba.druid.pool.DruidDataSourcespring.datasource.url=jdbc:mysql://localhost:3306/kuaidi?useUnicode=true&characterEncoding=UTF-8spring.datasource.username=root #数据库账号spring.datasource.password=root #数据库密码#MyBatis日志配置mybatis.mapperLocations=classpath:mapper/*.xmlmybatis.config-location=classpath:/config/mybatis-config.xml#端口配置server.port=8082# 定位模板的目录spring.mvc.view.prefix=classpath:/templates/# 给返回的页面添加后缀名spring.mvc.view.suffix=.html

创建数据库表 表结构如下:

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;

运行 将项目导入Idea工具,找到com.wangzg.kuaidi.KuaiDiApplication文件,执行main方法即可,如下图:

三、部署3.1 jar部署

上传安装包 在服务器创建/usr/myworkspace,执行下面命令可直接创建:

mkdir -p /usr/myworkspace复制代码

下载相关文件,上传到服务器/usr/myworkspace。下载地址:github.com/hellowHuaai… 文件主要包括:

application.properties 说明:项目配置文件,可能会涉及到修改服务器端口,数据库访问、端口、账号、密码等。kuaidi.jar 说明:后端服务的可执行jar文件。kuaidi.sql 说明:数据库初始化脚本。start.sh 说明: 启动服务器shell脚本。stop.sh 说明: 停止服务器shell脚本。

初始化数据库 打开Navicat工具,选中数据库,右键选择运行SQL文件...,具体操作,这样数据库就初始化完成。

运行项目

在服务器/usr/myworkspace目录下,执行如下命令,即可运行项目:

chmod +x *.sh #给所有 .sh文件添加执行权限./start.sh
3.2 Docker部署

Docker 容器化部署项目,需要创建一个 mysql 的容器,创建kuaidi的容器,再初始化一下数据库。

创建数据库容器 代码如下:

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

导入数据库脚本 数据库脚本kuaidi.sql内容如下:

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;

然后执行下面命令,就可以导入kuaidi.sql脚本:

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

创建kuaidi容器 执行下面命令就可以创建容器:

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

注:application.properties文件为项目的配置文件,在src/main/resources目录下;huaairen/kuaidi:latest是我打包好的镜像,直接下载就可以。

四、最后

项目功能还特别简陋,很多功能需要开发和完善。如果你也遇到类似的问题我们可以一起讨论,合作共赢哦!

作者:不安分的猿人

链接:

来源:掘金

标签: #cdnbootcss