使用SpringBoot+Vue+快递100API搭建一个快递查询网站
对接快递100API,使用springboot+vue+element-plus搭建一个前后端分离的快递查询网站项目
一、需求描述
1、需求
对接快递100快递查询接口,后端使用Springboot,前端使用vue2+element-plus,搭建一个简洁、美观、适配手机端PC端且前后端分离的快递查询网站项目。
2、工具
idea
3、项目准备
前往快递100API开放平台注册账号,注册用户有50个测试单量
4、效果
4.1 PC端效果展示
4.2 移动端效果展示
二、构建前端项目
1、安装vue脚手架
2、通过vue create创建项目
2.1 在项目构建目录下进入cmd黑窗口
2.2 输入命令创建vue项目
vue create 你的项目名
操作键指引:
按空格键选中
a子母键全选
i子母键反选
回车键确定到下一步
2.3 选择Manually select features,自己选择需要的功能
2.4 选择需要的配置项
- Babel 支持babel(选上)
- TypeScript 安装ts(选上)
- Progressive Web App (PWA) Support(一般不选)
- Router 路由模块(选上)
- Vuex 状态管理(需要用到就选上)
- CSS Pre-processors css预处理器(选上)
- Linter / Formatter 代码校验(选上)
- Unit Testing 单元测试(一般不需要)
- E2E Testing 端到端测试(一般不用)
2.5 选择vue的版本
该项目选择2.x
2.6 选择配置中选择了TypeScript,就会有这一步,class-style component syntax 是否使用class类风格编码,选y
2.7 是否使用history路由模式,默认是hash模式,hash模式会在url后面带#f符号,选y
2.8 选择css预处理器类型,选Saas/SCSS
2.9 选择一个代码校验配置支持代码风格检查和格式化,选ESLint with error prevention only
- ESLint with error prevention only (仅具有错误预防功能)
- ESLint + Airbnb config (Airbnb配置)
- ESLint + Standard config (标准配置)
- ESLint + Prettier (Prettier)
2.10 选择什么时候校验格式,选Lint on save
- Lint on save(保存时)
- Lint and fix on commit(提交时)
2.11 选择配置文件的位置,由于每个插件都有自己单独的配置文件,所以择第一个
2.12 否将当前配置选项保存起来,方便下次创建项目时使用,选n
2.13 vue项目创建成功
三、创建Springboot项目
1、创建一个初始化项目
2、创建完Spring项目后将vue项目移动到该项目下
项目结构如下图,其中kd-query-vue是vue项目
3、配置启动类
3.1 配置Springboot项目启动类
3.2 配置vue项目启动类
四、构建前端项目
1、项目结构
2、main.js导入项目需要的包
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
//mdeditor
import VueMarkdownEditor from '@kangc/v-md-editor';
import '@kangc/v-md-editor/lib/style/base-editor.css';
import vuepressTheme from '@kangc/v-md-editor/lib/theme/vuepress.js';
import '@kangc/v-md-editor/lib/theme/style/vuepress.css';
//中文包
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import Prism from 'prismjs';
//mdeditor
VueMarkdownEditor.use(vuepressTheme, {
Prism,
});
const app = createApp(App)
// 图标
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(store).use(VueMarkdownEditor).use(router).use(ElementPlus, {locale: zhCn,}).mount('#app')
3、新建utils/request.js用于发送axios请求
import axios from 'axios'
const request = axios.create({
baseURL: '/api',
timeout: 5000
})
request.interceptors.request.use(config => {
config.headers['Content-Type'] = 'application/json;charset=utf-8';
if (localStorage.getItem('token')) {
config.headers['token'] = localStorage.getItem('token');
}
return config
}, error => {
return Promise.reject(error)
});
request.interceptors.response.use(
response => {
let res = response.data;
if (response.config.responseType === 'blob') {
return res
}
if (typeof res === 'string') {
res = res ? JSON.parse(res) : res
}
return res;
},
error => {
console.log('err' + error);// for debug
return Promise.reject(error)
}
)
export default request
4、vue.config.js配置跨域请求
请求url与后端项目的请求地址对应
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true
});
// 跨域配置
module.exports = {
devServer: {
open: true,
host: 'localhost',
port: 8081,
proxy: {
'/api': {
target: 'http://localhost:8923',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
};
5、新建views/Query.vue作为查询主页面
5.1、此处的页面设计
1、输入框,用于输入单号
2、按键,点击进行查询
3、选择框,用于选择快递公司
4、输入框,用于输入收件人或寄件人姓名(顺丰查询快递需要)
5、物流轨迹展示盒子,超出自动滚动
6、存放所有物流轨迹的盒子,置于物流轨迹展示盒子中
5.2 其他
1、标题为快递侠
2、版权展示,居中靠近底部
5.3 注意
1、为了兼容移动端和PC端,页面中的样式长宽高大部分使用百分比
2、对输入框进行简单校验
3、单号输入框进行回车操作后即可进行查询
4、对消息提示进行封装
物流轨迹选择的是element-plus中的 Timeline-时间线,通过自定义节点样式达到我们物流轨迹的效果,其中我们需要自定义的主要样式有内容、时间、节点颜色、节点图标,我们可以在后端通过物流轨迹内容给物流节点的颜色和图标赋值。
5.4 页面代码
请求后端返回的查询结果格式:
{
"code": "200",
"msg": "成功",
"data": [
{
"content": "[河北省 石家庄市 裕华区]包裹已签收!(凭取件码)如有问题请电联:,投诉电话:0311-68048041",
"timestamp": "2023-01-15 11:47:56",
"icon": "Select",
"color": "green",
"code": 102
},
{
"content": "[河北省 石家庄市 裕华区]您的包裹已存放至【代收点】,记得早点来【恒大雅苑百世快递恒大雅苑百世快递】取它回家!如有问题请联系:,投诉电话:0311-68048041",
"timestamp": "2023-01-15 10:49:00",
"icon": null,
"color": null,
"code": 101
},
{
"content": "[河北省 石家庄市 裕华区]【石家庄化校网点】的兔兔快递员刘辉(13888888888)正在派件(可放心接听952300专属热线),投诉电话:0311-68048041。今天的兔兔,体温正常,口罩戴好,消毒到位,即将为您派件。",
"timestamp": "2023-01-15 08:28:57",
"icon": null,
"color": null,
"code": 101
},
{
"content": "[河北省 石家庄市 裕华区]快件到达【石家庄化校网点】",
"timestamp": "2023-01-15 08:20:57",
"icon": null,
"color": null,
"code": 101
},
{
"content": "[河北省 石家庄市 裕华区]快件离开【石家庄转运中心】已发往【石家庄化校网点】",
"timestamp": "2023-01-14 15:46:07",
"icon": null,
"color": null,
"code": 101
},
{
"content": "[河北省 石家庄市 裕华区]快件到达【石家庄转运中心】",
"timestamp": "2023-01-14 14:18:18",
"icon": null,
"color": null,
"code": 101
},
{
"content": "[福建省 泉州市 晋江市]快件离开【泉州转运中心】已发往【石家庄转运中心】",
"timestamp": "2023-01-13 03:12:30",
"icon": null,
"color": null,
"code": 101
},
{
"content": "[福建省 泉州市 晋江市]快件到达【泉州转运中心】",
"timestamp": "2023-01-13 03:08:07",
"icon": null,
"color": null,
"code": 101
},
{
"content": "[福建省 三明市 沙县]快件离开【三明转运中心】已发往【泉州转运中心】",
"timestamp": "2023-01-12 21:57:17",
"icon": null,
"color": null,
"code": 101
},
{
"content": "[福建省 三明市 沙县]快件到达【三明转运中心】",
"timestamp": "2023-01-12 21:54:58",
"icon": null,
"color": null,
"code": 101
},
{
"content": "[福建省 三明市 沙县]快件离开【三明市沙县一部集散点】已发往【三明转运中心】",
"timestamp": "2023-01-12 21:54:48",
"icon": null,
"color": null,
"code": 101
},
{
"content": "[福建省 南平市 建阳区]快件离开【南平建阳区网点】已发往【三明市沙县一部集散点】",
"timestamp": "2023-01-12 13:19:45",
"icon": null,
"color": null,
"code": 101
},
{
"content": "[福建省 南平市 建阳区]【南平建阳区网点】的练文斌(13888888888)已取件。投诉电话:0599-8500490",
"timestamp": "2023-01-12 13:13:45",
"icon": null,
"color": null,
"code": 101
}
]
}
Query.vue页面的代码如下:
<template>
<div class="common-layout">
<el-container>
<el-header style="padding: 0; margin: 0">
<div style="height: 60px; background: #6c8fce; padding-left: 15%">
<h3 style="height:60px; line-height:60px; color: white; font-size: 26px; font-weight: 500">快递侠</h3>
</div>
</el-header>
<el-main style="height: calc(100vh - 120px);">
<div class="user_input">
<div style="position:relative; width: 70%;margin-left: 15%; margin-bottom: 30px;margin-top: 30px">
<el-input
v-model="kuaidinum"
class="w-50 m-2 selfinput"
size="mini"
placeholder="输入单号"
@keyup.enter = "query"
/>
<el-button type="primary" class="self_btn" :icon="Search" @click="query">查询</el-button>
</div>
<div style="width: 70%;margin-left: 15%; margin-bottom: 30px">
<el-select v-model="kuaidicom" class="m-2" placeholder="选择快递公司">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
<div style="width: 70%;margin-left: 15%; margin-bottom: 30px;margin-top: 30px">
<el-input v-model="phone" placeholder="收寄件人电话号码(顺丰必填)" />
</div>
</div>
<div class="trackBox" style="width: 70%; margin-left: 15%; height: 400px; overflow: auto">
<el-timeline>
<el-timeline-item
v-for="(item,index) in trackData"
:key="index"
:icon="item.icon"
:color="item.color"
:timestamp="item.timestamp"
>
{{ item.content }}
</el-timeline-item>
</el-timeline>
</div>
</el-main>
<el-footer style="height: 60px">
<p style="height: 60px; line-height:60px; text-align: center; font-weight: 300; font-size: 12px">©2023 http://www.roud.top</p>
</el-footer>
</el-container>
</div>
</template>
<script>
import request from "../utils/request";
import {ElMessage} from "element-plus";
export default {
name: "Query",
data(){
return{
//快递公司选择框数据,value为发送表单时的值,键为对应的v-model,value值参考快递100API快递公司编码表(https://api.kuaidi100.com/manager/openapi/download/kdbm.do)
options : [
{
value: 'zhongtong',
label: '中通快递',
},
{
value: 'jtexpress',
label: '极兔速递',
},
{
value: 'yunatong',
label: '圆通速递',
},
{
value: 'shunfeng',
label: '顺丰速运',
},
{
value: 'yunda',
label: '韵达快递',
},
{
value: 'shentong',
label: '申通快递',
},
{
value: 'youzhengguonei',
label: '邮政速递',
},{
value: 'ems',
label: 'EMS',
},
{
value: 'jd',
label: '京东物流',
},
{
value: 'debangkuaidi',
label: '德邦快递',
},
{
value: 'debang物流',
label: '德邦物流',
},
{
value: 'fengwang',
label: '丰网速运',
},
{
value: 'sxjdfreight',
label: '顺心捷达',
},
{
value: 'zhaijisong',
label: '宅急送',
},
{
value: 'danniao',
label: '丹鸟',
},
{
value: 'shunfengkuaiyun',
label: '顺丰快运',
},
{
value: 'shunfengkuaiyun',
label: '顺丰快运',
},
{
value: 'disifang',
label: '递四方',
},{
value: 'zhuanyunsifang',
label: '转运四方',
},{
value: 'ups',
label: 'UPS',
},{
value: 'jinguangsudikuaijian',
label: '京广速递',
},
{
value: 'yimidida',
label: '壹米滴答',
},
{
value: 'shunfengkuaiyun',
label: '顺丰快运',
}
],
kuaidinum: "",
kuaidicom: "",
phone: "",
trackData: [],
form: {}
}
},
methods:{
//成功提示
showSuccessMessage(msg){
ElMessage.success({
message: msg,
});
},
//失败提示
showFailMessage(msg){
ElMessage.error({
message: msg,
});
},
//警告提示
showWarningMessage(msg){
ElMessage.warning({
message: msg,
});
},
//点击查询执行的方法
query(){
this.form = {}
this.trackData = []
//判断输入是否为空
if(this.kuaidinum === "" || this.kuaidinum == null || this.kuaidicom == null || this.kuaidicom === ""){
this.showWarningMessage("请输入准确的信息!");
return;
}
//对单号进行简单检验
var reg = /^[a-zA-Z]*[0-9]+$/
if(!reg.test(this.kuaidinum)){
this.showWarningMessage("请输入正确的单号!");
return;
}
this.form.num = this.kuaidinum;
this.form.com = this.kuaidicom;
if("shunfeng" === this.kuaidicom){
this.form.phone = this.phone;
}
//发送查询请求
request.post("/query/kd100", this.form).then(res => {
//处理查询结果
let data = res["data"];
if((data[0])["code"] === 101){
(data[0])["color"] = "green";
}
if(data.length > 1 && (data[data.length-1])["code"] === 101){
(data[data.length-1])["icon"] = "ArrowDownBold"
}
if((data[0])["code"] === 101 || (data[0])["code"] === 102){
this.showSuccessMessage("查询成功");
}else {
this.showWarningMessage("查询异常")
}
this.trackData = data.reverse();
});
}
}
}
</script>
<style>
*{
padding: 0;
margin: 0;
}
.el-select{
width: 100%;
}
.selfinput{
width: 90% !important;
}
.self_btn{
position: absolute;
right: 0 !important;
}
::-webkit-scrollbar {
height: 6px;
width: 3px;
}
::-webkit-scrollbar-track {
background-color: transparent;
}
::-webkit-scrollbar-thumb {
box-shadow: inset 0 0 0 rgba(240, 240, 240, .5);
border-radius: 2px;
background-color: rgba(240, 240, 240, .5);
}
</style>
6、router/index.js配置路由
配置默认路由及/kdx/query为该查询页面
import { createRouter, createWebHistory } from 'vue-router'
import Query from "@/views/Query";
const routes = [
{
path: '/kdx/query',
name: 'query',
component: Query,
},
{
path: '/',
name: 'q',
component: Query,
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
五、构建后端项目
1、项目结构
2、pom.xml导入所需依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>top.roud</groupId>
<artifactId>kd-query100</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>kd-query100</name>
<description>kd-query100</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.6</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.6</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.10</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
</project>
3、src/main/resources/application.yml配置文件
这里简单配置一下项目端口
server:
port: 8923
4、新建src/main/java/top/roud/kdquery100/utils包存放工具类
4.1 http包
用于存放http请求工具类
目录结构如下:
4.1.1 HttpResult请求结果封装类
package top.roud.kdquery100.utils.http;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @description : TODO
* @author: roud
* @date: 2023/1/4
* @version:
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class HttpResult {
private int status;
private String body;
private String error;
}
4.1.2 HttpUtil请求工具类
package top.roud.kdquery100.utils.http;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* @description : TODO
* @author: roud
* @date: 2023/1/4
* @version:
*/
public class HttpUtil {
public static HttpResult doPost(String url, Object obj, int connectTimeout, int socketTimeout) {
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
CloseableHttpResponse resp = null;
HttpResult result = new HttpResult();
try {
Map<String, String> params = ObjectToMapUtil.objectToMap(obj);
HttpPost httpPost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).build();
httpPost.setConfig(requestConfig);
if (params != null && params.size() > 0) {
List<NameValuePair> list = new ArrayList();
Iterator var11 = params.entrySet().iterator();
while(var11.hasNext()) {
Map.Entry<String, String> entry = (Map.Entry)var11.next();
list.add(new BasicNameValuePair((String)entry.getKey(), (String)entry.getValue()));
}
// httpPost.setHeader("Content-Type","application/x-www-form-urlencoded");
httpPost.setEntity(new UrlEncodedFormEntity(list, "UTF-8"));
}
resp = httpClient.execute(httpPost);
String body = EntityUtils.toString(resp.getEntity(), "UTF-8");
int statusCode = resp.getStatusLine().getStatusCode();
result.setStatus(statusCode);
result.setBody(body);
} catch (Exception var21) {
var21.printStackTrace();
} finally {
if (null != resp) {
try {
resp.close();
} catch (IOException var20) {
var20.printStackTrace();
}
}
}
return result;
}
}
4.1.3 ObjectToMapUtil
package top.roud.kdquery100.utils.http;
import java.lang.reflect.Field;
import java.util.*;
/**
* @description : TODO
* @author: roud
* @date: 2023/1/4
* @version:
*/
public class ObjectToMapUtil {
public ObjectToMapUtil() {
}
public static Map<String, String> objectToMap(Object obj) throws IllegalAccessException {
if (obj == null) {
return null;
} else {
Map<String, String> map = new HashMap();
List<Field> allField = getAllField(obj);
String fieldName;
String fieldValue;
for(Iterator var3 = allField.iterator(); var3.hasNext(); map.put(fieldName, fieldValue)) {
Field field = (Field)var3.next();
field.setAccessible(true);
fieldName = field.getName();
fieldValue = "";
if (field.getType() == String.class || field.getType() == Integer.class || field.getType() == Integer.TYPE) {
fieldValue = field.get(obj) == null ? "" : field.get(obj).toString();
}
}
return map;
}
}
private static List<Field> getAllField(Object obj) {
List<Field> fieldList = new ArrayList();
for(Class clazz = obj.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
fieldList.addAll(Arrays.asList(clazz.getDeclaredFields()));
}
return fieldList;
}
}
4.2 md包
主要存放md5加密的工具类
目录结构:
DigestUtil 加密工具类
package top.roud.kdquery100.utils.md;
import org.springframework.util.DigestUtils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
/**
* @description : TODO
* @author: roud
* @date: 2023/1/18
* @version:
*/
public class DigestUtil {
public static String md5(String str) {
String md5 = "";
try {
md5 = DigestUtils.md5DigestAsHex(str.getBytes("utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return md5;
}
}
4.3 rusult包
主要存放返回给用户的结果的封装类
目录结构:
4.3.1 StatusCode 返回结果状态码枚举类
package top.roud.kdquery100.utils.result;
/**
* @description : TODO
* @author: roud
* @date: 2023/1/6
* @version:
*/
public enum StatusCode {
SUCCESS("200","成功"),
ERROR("500","服务器错误"),
FAIL("501","查无结果"),
ACCOUNT_CHECK_FAIL("301","账号校验失败");
private String code;
public String getCode() {
return code;
}
private String message;
public String getMessage() {
return message;
}
StatusCode(String code, String message) {
this.code = code;
this.message = message;
}
}
4.3.2 StatusCode返回结果类
package top.roud.kdquery100.utils.result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @description : TODO
* @author: roud
* @date: 2023/1/6
* @version:
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
private String code;
private String msg;
private Object data;
public Result(StatusCode sc, Object data){
this.code = sc.getCode();
this.msg = sc.getMessage();
this.data = data;
}
}
5、新建src/main/java/top/roud/kdquery100/entity包存放实体类
目录结构如下:
根据快递100实时查询API技术文档的参数要求构建请求实体类,包括请求、请求参数、请求结果
5.1 QueryParam 查询参数类
package top.roud.kdquery100.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @description : TODO
* @author: roud
* @date: 2023/1/18
* @version:
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class QueryParam {
private String com;
private String num;
private String phone;
private String from;
private String to;
private String resultv2;
private String show;
private String order;
}
5.2 QueryRequest 查询请求类
package top.roud.kdquery100.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @description : TODO
* @author: roud
* @date: 2023/1/18
* @version:
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class QueryRequest {
private String customer;
private String sign;
private String param;
}
5.3 QueryResult 查询结果类
package top.roud.kdquery100.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @description : TODO
* @author: roud
* @date: 2023/1/19
* @version:
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class QueryResult {
private String content;
private String timestamp;
private String icon;
private String color;
/**
* 101 在途中
* 102 已签收
* 103 查无结果
* 104 服务器错误
*/
private Integer code;
}
6、业务类编写
目录结构:
6.1 QueryServiceImpl 查询业务接口
package top.roud.kdquery100.service.impl;
import top.roud.kdquery100.entity.QueryParam;
import top.roud.kdquery100.utils.result.Result;
/**
* @description : TODO
* @author: roud
* @date: 2023/1/18
* @version:
*/
public interface QueryServiceImpl {
public Result queryKdTrack(QueryParam param);
}
6.2 QueryService 查询业务实现类
需要前往快递100API开放平台注册账号,注册用户有50个测试单量。在我的信息-企业信息中获取授权Key和customer替换填入代码中
代码如下,根据完成的前端项目所需的JSON格式进行构造:
package top.roud.kdquery100.service;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import top.roud.kdquery100.entity.QueryParam;
import top.roud.kdquery100.entity.QueryRequest;
import top.roud.kdquery100.entity.QueryResult;
import top.roud.kdquery100.service.impl.QueryServiceImpl;
import top.roud.kdquery100.utils.http.HttpResult;
import top.roud.kdquery100.utils.http.HttpUtil;
import top.roud.kdquery100.utils.md.DigestUtil;
import top.roud.kdquery100.utils.result.Result;
import top.roud.kdquery100.utils.result.StatusCode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* @description : TODO
* @author: roud
* @date: 2023/1/18
* @version:
*/
@Service
public class QueryService implements QueryServiceImpl{
@Override
public Result queryKdTrack(QueryParam param) {
String paramStr = JSONObject.toJSONString(param);
QueryRequest queryRequest = new QueryRequest();
queryRequest.setParam(paramStr);
queryRequest.setCustomer("快递100API企业管理后台customer");
String sign = DigestUtil.md5(paramStr + "快递100API企业管理后台key" + "快递100API企业管理后台customer").toUpperCase();
queryRequest.setSign(sign);
ArrayList<QueryResult> list = new ArrayList<>();
try{
HttpResult httpResult = HttpUtil.doPost("https://poll.kuaidi100.com/poll/query.do", queryRequest, 3000, 300);
if(httpResult.getStatus() == 200){
String body = httpResult.getBody();
JSONObject jsonObject = JSONObject.parseObject(body);
if(StringUtils.contains(body, "\"status\":\"200\"")){
JSONArray datas = jsonObject.getJSONArray("data");
List<QueryResult> resList = datas.stream().map(o -> {
QueryResult queryResult = new QueryResult();
String ftime = ((JSONObject) JSONObject.toJSON(o)).getString("ftime");
String content = ((JSONObject) JSONObject.toJSON(o)).getString("context");
queryResult.setContent(content);
if(StringUtils.contains(content,"签收")){
queryResult.setIcon("Select");
queryResult.setColor("green");
queryResult.setCode(102);
}else {
queryResult.setCode(101);
}
queryResult.setTimestamp(ftime);
return queryResult;
}).collect(Collectors.toList());
return new Result(StatusCode.SUCCESS,resList);
}else{
return getResultList(list,"查无结果", "orange", "SemiSelect", 103, StatusCode.FAIL);
}
}
}catch (Exception e){
return getResultList(list,"服务器异常,请联系管理员修复", "red", "CloseBold",104, StatusCode.ERROR);
}
return getResultList(list,"服务器异常,请联系管理员修复", "red", "CloseBold",104, StatusCode.ERROR);
}
private Result getResultList(ArrayList<QueryResult> list, String content, String color, String icon, Integer code, StatusCode sc) {
String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
QueryResult queryResult = new QueryResult();
queryResult.setTimestamp(time);
queryResult.setContent(content);
queryResult.setIcon(icon);
queryResult.setColor(color);
queryResult.setCode(code);
list.add(queryResult);
return new Result(sc, list);
}
}
7、 控制层
目录结构:
QueryController 查询控制层类,定义的接口入口为/query/kd100
package top.roud.kdquery100.controller;
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 top.roud.kdquery100.entity.QueryParam;
import top.roud.kdquery100.service.impl.QueryServiceImpl;
import top.roud.kdquery100.utils.result.Result;
/**
* @description : TODO
* @author: roud
* @date: 2023/1/18
* @version:
*/
@RestController
@RequestMapping("query")
public class QueryController {
@Autowired
private QueryServiceImpl queryService;
@PostMapping("/kd100")
public Result query(@RequestBody QueryParam param){
return queryService.queryKdTrack(param);
}
}
六、前后端联调测试
先启动后端项目,再启动前端项目。
实现效果如下:
项目完成,撒花~~~
七、项目地址
更多推荐
所有评论(0)