温馨提示:本案例图文并茂,代码齐全(包括前端)大家只需要跟着文章一步步学习即可实现。想要一步到位直接获取代码的同学,请关注微信公众号「哈喽沃德先生」回复 logistics
即可。
电商购物或者快递发送相信每个人都经历过,每个订单的背后都关联着一连串的物流信息,每到达一个地点更新一次,直到用户签收为止。
普通人可能只享受收快递时的那种喜悦了,身为一名合格的程序员必须搞清楚其背后的实现原理。安排!
本文将通过 Spring Boot + MongoDB + Layui 实现一个简易版的物流订单管理系统,方便大家理解其背后的原理。
案例分析
正好最近使用顺丰快递了一批东西回老家,参考顺丰的物流流程大致如下:
根据上图可以得知如果要实现这个功能,采用关系型数据库最起码要两张表:订单表和物流表。
MySQL
订单表 order
物流表 logistics
SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
SELECT
o.id,
o.`status`,
o.order_time,
o.shipper,
o.shipper_address,
o.shipper_phone,
o.ship_time,
o.receiver,
o.receiver_address,
o.receiver_phone,
l.operation,
l.operator,
l.phone,
l.address,
l.operation_time,
l.details
FROM
`order` o
LEFT JOIN logistics l ON o.id = l.order_id
WHERE
o.id = 1368198081836879872;
|
通过分析发现关系型数据库完全可以胜任这个工作,但是随着物流运输的过程,订单表需要不断的更新,物流表需要不断的添加新的记录。眼光放长远去看,例如顺丰这样的公司,全国每天下单量总和可能会几百万甚至几千万(注意是每天),在这样的体量下,肯定会有性能瓶颈而需要优化,且存储在关系型数据库费钱(存储成本)又费力(优化成本)。
MongoDB
如果使用 MongoDB 来做这件事,上述问题都会迎刃而解:
- 体量问题(支持单机、主从集群、副本集集群、分片集群,提供冗余及自动故障转移,可以动态添加或移除节点)
- 成本问题(可以存储大尺寸、低价值数据)
- 优化问题(完整的索引支持,BSON 结构对象存储,可以动态添加、修改、删除字段,操作更方便)
- 性能问题(底层 C++ 编写,性能优秀)
- 学习问题(部署简单,Java 程序员配合 Spring Data 轻松上手)
比如刚才两张表的数据,可以直接存储在一个集合中(嵌套文档)即可。假设后期市场需求变更需要添加字段,莫急,随时添加随时使用。数据越存越多,不慌,自动分片功能支持水平的数据库集群,可动态添加额外的机器。并且 MongoDB 支持 GIS 功能,非常适用于 MongoDB 来支撑物流业务。
综上所述,你懂的,废话不多说,下面进入实战环节。
下图来自:https://db-engines.com/en/ranking
准备工作
环境
- MongoDB:4.4.4,安装请参考:分布式文件存储数据库 MongoDB
- Spring Boot:2.4.3
- JDK:11.0.7
- 前端:Layui 2.5.7,文中配有详细代码
- IDE:IntelliJ IDEA
MongoDB
为了方便省事,直接使用可视化客户端管理工具创建一个 example
数据库。
Spring Boot
创建项目
使用 Spring Initializr
初始化 Spring Boot 项目,添加 Spring Web
,Spring Data MongoDB
,Lombok
。
顺便再添加 hutool
工具集,方便根据雪花算法生成订单编号。
1
2
3
4
5
|
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.5.9</version>
</dependency>
|
配置文件
application.yml 配置 MongoDB 服务器的 example 数据库。
1
2
3
4
|
spring:
data:
mongodb:
uri: mongodb://192.168.10.101:27017/example
|
前端
使用 CDN 替换 CSS、JS 免去下载文件的过程。
1
2
|
<link href="//lib.baomitu.com/layui/2.5.7/css/layui.min.css" rel="stylesheet">
<script src="//lib.baomitu.com/layui/2.5.7/layui.min.js"></script>
|
index.html
参考:https://www.layui.com/doc/element/layout.html#admin
index.html 最终修改如下,将该文件放入项目 resources
目录下的 static
目录中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>物流订单管理系统</title>
<link href="//lib.baomitu.com/layui/2.5.7/css/layui.min.css" rel="stylesheet">
</head>
<body class="layui-layout-body">
<div class="layui-layout layui-layout-admin">
<div class="layui-header">
<div class="layui-logo">物流订单管理系统</div>
<!-- 头部区域(可配合layui已有的水平导航) -->
<ul class="layui-nav layui-layout-right">
<li class="layui-nav-item">
<a href="javascript:;">
<img src="https://mrhelloworld.com/resources/mrhelloworld/logo/avatar.jpg" class="layui-nav-img">
哈喽沃德先生
</a>
<dl class="layui-nav-child">
<dd><a href="">基本资料</a></dd>
<dd><a href="">安全设置</a></dd>
</dl>
</li>
<li class="layui-nav-item"><a href="">安全退出</a></li>
</ul>
</div>
<div class="layui-side layui-bg-black">
<div class="layui-side-scroll">
<!-- 左侧导航区域(可配合layui已有的垂直导航) -->
<ul class="layui-nav layui-nav-tree" lay-filter="test">
<li class="layui-nav-item layui-nav-itemed">
<a class="" href="javascript:;">订单管理</a>
<dl class="layui-nav-child">
<dd><a href="order-manage.html" target="container">订单管理</a></dd>
<dd><a href="add-order.html" target="container">订单添加</a></dd>
<dd><a href="add-logistics.html" target="container">物流添加</a></dd>
</dl>
</li>
</ul>
</div>
</div>
<div class="layui-body">
<!-- 内容主体区域 -->
<iframe src="add-order.html" name="container" width="100%" height="100%"></iframe>
</div>
<div class="layui-footer">
<!-- 底部固定区域 -->
https://mrhelloworld.com - 哈喽沃德先生
</div>
</div>
<script src="//lib.baomitu.com/layui/2.5.7/layui.min.js"></script>
<script>
//JavaScript代码区域
layui.use('element', function () {
var element = layui.element;
});
</script>
</body>
</html>
|
add-order.html
创建文件先写个订单添加等后续功能开发时再做处理。
将该文件放入项目 resources
目录下的 static
目录中。
add-logistics.html
创建文件先写个物流添加等后续功能开发时再做处理。
将该文件放入项目 resources
目录下的 static
目录中。
order-manage.html
创建文件先写个订单管理等后续功能开发时再做处理。
将该文件放入项目 resources
目录下的 static
目录中。
启动
访问:http://localhost:8080/index.html 效果如下:
功能开发
实体类
Order.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
package com.example.pojo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order implements Serializable {
private static final long serialVersionUID = 2298879574024937430L;
private String id; // 订单编号
private String status; // 订单状态
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date orderTime; // 下单时间
private String shipper; // 发货人
private String shipperAddress; // 发货人地址
private String shipperPhone; // 发货人电话
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date shipTime; // 发货时间
private String receiver; // 收货人
private String receiverAddress; // 收货人地址
private String receiverPhone; // 收货人电话
private List<Logistics> logistics; // 物流信息
}
|
Logistics.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package com.example.pojo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Logistics implements Serializable {
private static final long serialVersionUID = -7416584494933994605L;
private String orderId; // 订单编号
private String operation; // 操作名称
private String operator; // 操作员
private String phone; // 操作员电话
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date operationTime; // 操作时间
private String address; // 操作地址
private String details; // 详细信息
}
|
订单添加
OrderService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
package com.example.service;
import cn.hutool.core.util.IdUtil;
import com.example.pojo.Order;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;
/**
* @author 哈喽沃德先生
* @微信公众号 哈喽沃德先生
* @website https://mrhelloworld.com
* @wechat 124059770
*/
@Service
public class OrderService {
@Resource
private MongoTemplate mongoTemplate;
/**
* 添加订单至 MongoDB
*
* @param order
*/
public void addOrder(Order order) {
// 订单编号根据雪花算法生成
order.setId(IdUtil.getSnowflake(1, 1).nextIdStr());
// 设置订单状态
order.setStatus("已下单");
// 设置下单时间
order.setOrderTime(new Date());
// 设置发货时间
order.setShipTime(new Date());
// 添加订单至 MongoDB
mongoTemplate.insert(order, "order");
}
}
|
OrderController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
package com.example.controller;
import com.example.pojo.Order;
import com.example.service.OrderService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author 哈喽沃德先生
* @微信公众号 哈喽沃德先生
* @website https://mrhelloworld.com
* @wechat 124059770
*/
@RestController
@RequestMapping("order")
public class OrderController {
@Resource
private OrderService orderService;
/**
* 添加订单至 MongoDB
*
* @param order
*/
@PostMapping("add")
public String addOrder(Order order) {
orderService.addOrder(order);
return "订单添加成功";
}
}
|
add-order.html
参考:
add-order.html 最终修改如下。
注意:form 表单中 input 标签的 name 属性值对应实体类 Order 的属性名。记得添加 jQuery。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>订单添加</title>
<link href="//lib.baomitu.com/layui/2.5.7/css/layui.min.css" rel="stylesheet">
</head>
<body>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
<legend>订单添加</legend>
</fieldset>
<div class="layui-card">
<div class="layui-card-body">
<form class="layui-form" action="">
<div class="layui-form-item">
<label class="layui-form-label">发货人</label>
<div class="layui-input-block">
<input type="text" name="shipper" autocomplete="off" class="layui-input" value="">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">发货人地址</label>
<div class="layui-input-block">
<input type="text" name="shipperAddress" autocomplete="off" class="layui-input" value="">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">发货人电话</label>
<div class="layui-input-block">
<input type="text" name="shipperPhone" autocomplete="off" class="layui-input" value="">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">收货人</label>
<div class="layui-input-block">
<input type="text" name="receiver" autocomplete="off" class="layui-input" value="">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">收货人地址</label>
<div class="layui-input-block">
<input type="text" name="receiverAddress" autocomplete="off" class="layui-input" value="">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">收货人电话</label>
<div class="layui-input-block">
<input type="text" name="receiverPhone" autocomplete="off" class="layui-input" value="">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="formDemo">提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</form>
</div>
</div>
<script src="//lib.baomitu.com/jquery/3.6.0/jquery.min.js"></script>
<script src="//lib.baomitu.com/layui/2.5.7/layui.min.js"></script>
<script>
//Demo
layui.use('form', function () {
var form = layui.form;
//监听提交
form.on('submit(formDemo)', function (data) {
$.ajax({
url: "/order/add",
type: "POST",
data: data.field,
dataType: "JSON",
success: function (result) {
layer.msg(result);
}
});
return false;
});
});
</script>
</body>
|
测试
访问:http://localhost:8080/index.html 填写表单信息并提交。
MongoDB 数据库。
物流添加(订单更新)
OrderService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
/**
* 更新订单信息
* 追加物流信息
*
* @param logistics
*/
public void updateOrderAndAddLogistics(Logistics logistics) {
// 获取操作名称
String status = logistics.getOperation();
// 设置操作时间
logistics.setOperationTime(new Date());
// 初始化 Query 对象,根据订单编号查询
Query query = new Query(Criteria.where("_id").is(logistics.getOrderId()));
// 初始化 Update 对象
Update update = new Update();
// 更新订单状态
update.set("status", status);
// 追加物流信息,物流信息由多个物流数据组成,使用 push() 追加
update.push("logistics", logistics);
// 更新订单信息
mongoTemplate.upsert(query, update, Order.class, "order");
}
|
OrderController.java
1
2
3
4
5
6
7
8
9
10
11
|
/**
* 更新订单信息
* 添加物流信息
*
* @param logistics
*/
@PostMapping("update")
public String updateOrderAndAddLogistics(Logistics logistics) {
orderService.updateOrderAndAddLogistics(logistics);
return "物流添加成功";
}
|
add-logistics.html
参考:
add-logistics.html 最终修改如下。
注意:form 表单中 input 标签的 name 属性值对应实体类 Logistics 的属性名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>物流添加</title>
<link href="//lib.baomitu.com/layui/2.5.7/css/layui.min.css" rel="stylesheet">
</head>
<body>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
<legend>物流添加</legend>
</fieldset>
<div class="layui-card">
<div class="layui-card-body">
<form class="layui-form" action="">
<div class="layui-form-item">
<label class="layui-form-label">订单编号</label>
<div class="layui-input-block">
<input type="text" name="orderId" autocomplete="off" class="layui-input" value="">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">操作名称</label>
<div class="layui-input-block">
<select name="operation" lay-filter="aihao">
<option value="">请选择</option>
<option value="已取件">已取件</option>
<option value="运送中">运送中</option>
<option value="派送中">派送中</option>
<option value="已签收">已签收</option>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">操作员</label>
<div class="layui-input-block">
<input type="text" name="operator" autocomplete="off" class="layui-input" value="">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">操作员电话</label>
<div class="layui-input-block">
<input type="text" name="phone" autocomplete="off" class="layui-input" value="">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">操作地址</label>
<div class="layui-input-block">
<input type="text" name="address" autocomplete="off" class="layui-input" value="">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">详细信息</label>
<div class="layui-input-block">
<input type="text" name="details" autocomplete="off" class="layui-input" value="">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="formDemo">提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</form>
</div>
</div>
<script src="//lib.baomitu.com/jquery/3.6.0/jquery.min.js"></script>
<script src="//lib.baomitu.com/layui/2.5.7/layui.min.js"></script>
<script>
// Demo
layui.use('form', function () {
var form = layui.form;
//监听提交
form.on('submit(formDemo)', function (data) {
$.ajax({
url: "/order/update",
type: "POST",
data: data.field,
dataType: "JSON",
success: function (result) {
layer.msg(result);
}
});
return false;
});
});
</script>
</body>
|
测试
访问:http://localhost:8080/index.html 选择物流添加,填写表单信息并提交。
提示:操作员、操作员电话、操作地址是对物流信息的一个后台记录,用户界面无需展示。
MongoDB 数据库。
模拟真实物流
接下来让我们模拟真实物流,多添加一些物流信息。
- 已取件:顺丰速运已收取快件
- 运送中:快件在【北京市海淀区XX营业点】完成分拣,准备发往【北京海淀集散点】
- 运送中:快件已发车
- 运送中:快件到达【北京海淀集散点】
- 运送中:快件在【北京海淀集散点】完成分拣,准备发往【北京中转场】
- 运送中:快件已发车
- 运送中:快件到达【北京中转场】
- 运送中:快件在【北京中转场】完成分拣,准备发往【上海中转场】
- 运送中:快件已发车
- 运送中:快件到达【上海中转场】
- 运送中:快件在【上海中转场】完成分拣,准备发往【上海浦东新区集散点】
- 运送中:快件已发车
- 运送中:快件到达【上海浦东新区集散点】
- 运送中:快件在【上海浦东新区集散点】完成分拣,准备发往【上海市浦东新区集配站】
- 运送中:快件已发车
- 运送中:快件到达【上海市浦东新区集配站】
- 派送中:快件交给小顺子,正在派送途中(联系电话:1300000000,顺丰已开启“安全呼叫”保护您的电话隐私,请放心接听!)
- 已签收:您的快件已签收,如有疑问请电话联系快递员【小顺子,电话:1300000000】。疫情期间顺丰每日对网点消毒、快递员每日测温、佩戴口罩,感谢您使用顺丰,期待再次为您服务。
订单管理 - 查询
主要实现根据订单编号查询订单信息,前端展示订单信息和物流信息。
OrderService.java
1
2
3
4
5
6
7
8
9
10
11
|
/**
* 通过订单编号查询
*
* @param id
* @return
*/
public Order selectOrderById(String id) {
// 初始化 Query 对象,根据订单编号查询
Query query = new Query(Criteria.where("_id").is(id));
return mongoTemplate.findOne(query, Order.class, "order");
}
|
OrderController.java
1
2
3
4
5
6
7
8
9
10
|
/**
* 通过订单编号查询
*
* @param id
* @return
*/
@GetMapping("{id}")
public Order selectOrderById(@PathVariable String id) {
return orderService.selectOrderById(id);
}
|
order-manage.html
参考:
order-manage.html 最终修改如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>订单管理</title>
<link href="//lib.baomitu.com/layui/2.5.7/css/layui.min.css" rel="stylesheet">
</head>
<body>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
<legend>订单管理</legend>
</fieldset>
<div class="layui-card">
<div class="layui-card-body">
<div class="layui-inline">
<label class="layui-form-label">订单编号</label>
<div class="layui-input-inline">
<input type="text" id="orderId" name="orderId" autocomplete="off" class="layui-input" value="">
</div>
<button onclick="search();" type="button" class="layui-btn"><i class="layui-icon"></i></button>
</div>
</div>
</div>
<div style="padding: 20px; background-color: #F2F2F2;">
<div class="layui-row layui-col-space15">
<div class="layui-col-md12">
<div class="layui-card">
<div class="layui-card-header">订单信息</div>
<div class="layui-card-body" id="order">
</div>
</div>
</div>
<div class="layui-col-md12">
<div class="layui-card">
<div class="layui-card-header">物流信息</div>
<div class="layui-card-body">
<ul class="layui-timeline" id="logistics">
</ul>
</div>
</div>
</div>
</div>
</div>
<script src="//lib.baomitu.com/jquery/3.6.0/jquery.min.js"></script>
<script src="//lib.baomitu.com/layui/2.5.7/layui.min.js"></script>
<script type="text/javascript">
// 搜索
function search() {
// 获取订单编号
var orderId = $("#orderId").val();
// 每次查询前将订单信息和物流信息清空
$("#order").html("");
$("#logistics").html("");
// 异步请求
$.ajax({
url: "/order/" + orderId,
type: "GET",
dataType: "JSON",
// 拼接处理返回结果
success: function (order) {
if (order == null || order == undefined || order == "") {
layer.msg("订单不存在");
return;
}
// 处理订单信息
$("#order").append("订单编号:" + orderId + "(" + order["status"] + ")<hr/>")
.append("发货人:" + order["shipper"] + "<br/>")
.append("发货人地址:" + order["shipperAddress"] + "<br/>")
.append("发货人电话:" + order["shipperPhone"] + "<br/>")
.append("下单时间:" + order["orderTime"] + "<br/>")
.append("发货时间:" + order["shipTime"] + "<hr/>")
.append("收货人:" + order["receiver"] + "<br/>")
.append("收获人地址:" + order['receiverAddress'] + "<br/>")
.append("收获人手机:" + order["receiverPhone"]);
// 获取物流信息
let logistics = order['logistics'];
// 倒序循环
for (var i = logistics.length - 1; i >= 0; i--) {
// 处理物流信息
$("#logistics").append('<li class="layui-timeline-item">' +
'<i class="layui-icon layui-timeline-axis"></i>' +
'<div class="layui-timeline-content layui-text">' +
'<h3 class="layui-timeline-title">' +
'(' + logistics[i].operation + ')' +
logistics[i].operationTime + '</h3>' +
'<p>' + logistics[i].details + '</p>' +
'<p>操作员:' + logistics[i].operator + ' ' +
'操作员电话:' + logistics[i].phone + ' ' +
'操作地址:' + logistics[i].address + '</p></div></li>');
}
},
error: function (order) {
layer.msg(order);
}
});
}
</script>
</body>
|
测试
访问:http://localhost:8080/index.html 选择订单管理,填写订单编号进行搜索。
提示:操作员、操作员电话、操作地址是对物流信息的一个后台记录,用户界面无需展示。
订单管理 - 删除
先查询订单列表然后根据订单编号实现订单删除。
OrderService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/**
* 查询所有订单
*
* @return
*/
public List<Order> selectOrderList() {
return mongoTemplate.findAll(Order.class, "order");
}
/**
* 根据订单编号删除订单记录
*
* @param id
* @return
*/
public boolean deleteOrderById(String id) {
Query query = new Query(Criteria.where("_id").is(id));
DeleteResult result = mongoTemplate.remove(query, Order.class, "order");
return result.getDeletedCount() > 0 ? true : false;
}
|
OrderController.java
订单列表使用 Layui 数据表格展示,Layui 需要特定的 JSON 格式(下图为官方 DEMO),按照要求封装数据返回。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
/**
* 查询所有订单
*
* @return
*/
@GetMapping("list")
public Map<String, Object> selectOrderList() {
List<Order> list = orderService.selectOrderList();
Map<String, Object> map = new HashMap<>();
map.put("code", "0");
map.put("count", list.size());
map.put("data", list);
return map;
}
/**
* 根据订单编号删除订单记录
*
* @param id
* @return
*/
@PostMapping("delete")
public String deleteById(String id) {
orderService.deleteOrderById(id);
return "删除成功";
}
|
order-manage.html
参考:
order-manage.html 添加以下代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
<!-- 数据表格 -->
<div class="layui-card">
<div class="layui-card-header">订单列表</div>
<div class="layui-card-body">
<table class="layui-hide" id="orderList" lay-filter="orderTable"></table>
</div>
</div>
<!-- 筛选/导出/打印 -->
<script type="text/html" id="toolbarDemo">
</script>
<!-- 编辑/删除 -->
<script type="text/html" id="barDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="update">编辑</button>
<button class="layui-btn layui-btn-danger layui-btn-sm" lay-event="delete">删除</button>
</div>
</script>
<script type="text/javascript">
// 表格
layui.use('table', function () {
var table = layui.table;
table.render({
elem: '#orderList',
url: '/order/list',
cellMinWidth: 80, // 全局定义常规单元格的最小宽度,layui 2.2.1 新增
toolbar: '#toolbarDemo', // 筛选/导出/打印
cols: [[
{field: 'id', title: '订单编号', sort: true, fixed: 'left'},
{field: 'status', title: '订单状态'},
{field: 'orderTime', title: '下单时间', sort: true},
{field: 'shipper', title: '发货人'},
{field: 'shipperAddress', title: '发货地址'},
{field: 'shipperPhone', title: '发货人电话'},
{field: 'receiver', title: '收货人', edit: 'text'},
{field: 'receiverAddress', title: '收货地址'},
{field: 'receiverPhone', title: '收货人电话'},
{fixed: 'right', title: '操作', toolbar: '#barDemo'} // 编辑/删除
]]
});
// 监听事件
// on() 监听 lay-event="" 的元素时触发(该事件为 layui 2.4.0 开始新增)
// tool() 监听 lay-filter="" 的元素
// 注:tool 是工具条事件名,orderTable 是 table 原始容器的属性 lay-filter="对应的值"
table.on('tool(orderTable)', function (obj) {
var data = obj.data; // 获得当前行数据
// 获得 lay-event 对应的值进行比较
if (obj.event === 'delete') {
layer.confirm('该操作不可逆,确定删除?', function (index) {
$.ajax({
url: '/order/delete',
method: 'POST',
data: {'id': data.id},
dataType: "JSON",
traditional: true,
success: function (msg) {
layer.msg(msg);
obj.del(); // 删除对应行(tr)的DOM结构,并更新缓存
},
error: function (msg) {
layer.msg(msg)
}
});
layer.close(index);
});
}
});
});
</script>
|
测试
访问:http://localhost:8080/index.html 选择订单管理,点击表格中对应订单的删除按钮。
MongoDB 数据库。
结语
至此 MongoDB 的实战小项目《物流订单管理系统》就完成啦,本文讲解了 Spring Boot 整合 MongoDB 的 CRUD 操作,顺便结合前端 Layui 实现了简单的页面效果。作为一款非常热门的非关系型数据库,大家非常有必要进行更深入的学习,最后祝大家加薪!加薪!加薪!
温馨提示:本案例图文并茂,代码齐全(包括前端)大家只需要跟着文章一步步学习即可实现。想要一步到位直接获取代码的同学,请关注微信公众号「哈喽沃德先生」回复 logistics
即可。
参考
本文采用 知识共享「署名-非商业性使用-禁止演绎 4.0 国际」许可协议
。
大家可以通过 分类
查看更多关于 MongoDB
的文章。
🤗 您的点赞
和转发
是对我最大的鼓励和支持。
📢 扫码关注 哈喽沃德先生
「文档 + 视频」每篇文章都配有专门视频讲解,学习更轻松噢 ~