个人 二维码




公众号二维码

目录

为什么我推荐你使用 MongoDB 实现物流订单系统


温馨提示:本案例图文并茂,代码齐全(包括前端)大家只需要跟着文章一步步学习即可实现。想要一步到位直接获取代码的同学,请关注微信公众号「哈喽沃德先生」回复 logistics 即可

  电商购物或者快递发送相信每个人都经历过,每个订单的背后都关联着一连串的物流信息,每到达一个地点更新一次,直到用户签收为止。

  普通人可能只享受收快递时的那种喜悦了,身为一名合格的程序员必须搞清楚其背后的实现原理。安排!

  本文将通过 Spring Boot + MongoDB + Layui 实现一个简易版的物流订单管理系统,方便大家理解其背后的原理。

  

https://mrhelloworld.com/resources/articles/why/mongodb/727438095.jpg

案例分析

  

  正好最近使用顺丰快递了一批东西回老家,参考顺丰的物流流程大致如下:

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306212609651.png

  根据上图可以得知如果要实现这个功能,采用关系型数据库最起码要两张表:订单表和物流表。

  

MySQL

  

订单表 order

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306215242390.png

  

物流表 logistics

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306215304658.png

  

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;
https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306222153526.png

  通过分析发现关系型数据库完全可以胜任这个工作,但是随着物流运输的过程,订单表需要不断的更新,物流表需要不断的添加新的记录。眼光放长远去看,例如顺丰这样的公司,全国每天下单量总和可能会几百万甚至几千万(注意是每天),在这样的体量下,肯定会有性能瓶颈而需要优化,且存储在关系型数据库费钱(存储成本)又费力(优化成本)。

  

MongoDB

  

  如果使用 MongoDB 来做这件事,上述问题都会迎刃而解:

  • 体量问题(支持单机、主从集群、副本集集群、分片集群,提供冗余及自动故障转移,可以动态添加或移除节点)
  • 成本问题(可以存储大尺寸、低价值数据)
  • 优化问题(完整的索引支持,BSON 结构对象存储,可以动态添加、修改、删除字段,操作更方便)
  • 性能问题(底层 C++ 编写,性能优秀)
  • 学习问题(部署简单,Java 程序员配合 Spring Data 轻松上手)

  

  比如刚才两张表的数据,可以直接存储在一个集合中(嵌套文档)即可。假设后期市场需求变更需要添加字段,莫急,随时添加随时使用。数据越存越多,不慌,自动分片功能支持水平的数据库集群,可动态添加额外的机器。并且 MongoDB 支持 GIS 功能,非常适用于 MongoDB 来支撑物流业务。

  综上所述,你懂的,废话不多说,下面进入实战环节。

  下图来自:https://db-engines.com/en/ranking

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306232608614.png

  

准备工作

  

环境

  

  

MongoDB

  

  为了方便省事,直接使用可视化客户端管理工具创建一个 example 数据库。

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306153825236.png
https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306153933567.png

  

Spring Boot

  

创建项目

  

  使用 Spring Initializr 初始化 Spring Boot 项目,添加 Spring WebSpring Data MongoDBLombok

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306151255231.png
https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306152209563.png
https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306151809406.png

  顺便再添加 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

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210304171745254.png

  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 目录中。

1
订单添加

  

add-logistics.html

  

  创建文件先写个物流添加等后续功能开发时再做处理。

  将该文件放入项目 resources 目录下的 static 目录中。

1
物流添加

  

order-manage.html

  

  创建文件先写个订单管理等后续功能开发时再做处理。

  将该文件放入项目 resources 目录下的 static 目录中。

1
订单管理

  

启动

  

  访问:http://localhost:8080/index.html 效果如下:

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306162405698.png

  

功能开发

  

实体类

  

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

  

  参考:

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306160951655.png

  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 填写表单信息并提交。

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306164605216.png

  MongoDB 数据库。

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306171358653.png

  

物流添加(订单更新)

  

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

  

  参考:

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306160951655.png

  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 选择物流添加,填写表单信息并提交。

  提示:操作员、操作员电话、操作地址是对物流信息的一个后台记录,用户界面无需展示。

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306171018623.png

  MongoDB 数据库。

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306171514092.png

  

模拟真实物流

  

  接下来让我们模拟真实物流,多添加一些物流信息。

  • 已取件:顺丰速运已收取快件
  • 运送中:快件在【北京市海淀区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

  

  参考:

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306172257842.png

  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">&#xe615;</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 + '&nbsp;' +
              '操作员电话:' + logistics[i].phone + '&nbsp;' +
              '操作地址:' + logistics[i].address + '</p></div></li>');
        }
      },
      error: function (order) {
        layer.msg(order);
      }
    });
  }
</script>
</body>

  

测试

  

  访问:http://localhost:8080/index.html 选择订单管理,填写订单编号进行搜索。

  提示:操作员、操作员电话、操作地址是对物流信息的一个后台记录,用户界面无需展示。

https://mrhelloworld.com/resources/articles/why/mongodb/20210306184015.png

  

订单管理 - 删除

  

  先查询订单列表然后根据订单编号实现订单删除。

  

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),按照要求封装数据返回。

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306202053178.png
 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

  

  参考:

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306200326232.png

  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 选择订单管理,点击表格中对应订单的删除按钮。

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210307000404287.png
https://mrhelloworld.com/resources/articles/why/mongodb/image-20210307000437873.png

  MongoDB 数据库。

https://mrhelloworld.com/resources/articles/why/mongodb/image-20210306203230613.png

结语

  

  至此 MongoDB 的实战小项目《物流订单管理系统》就完成啦,本文讲解了 Spring Boot 整合 MongoDB 的 CRUD 操作,顺便结合前端 Layui 实现了简单的页面效果。作为一款非常热门的非关系型数据库,大家非常有必要进行更深入的学习,最后祝大家加薪!加薪!加薪!

温馨提示:本案例图文并茂,代码齐全(包括前端)大家只需要跟着文章一步步学习即可实现。想要一步到位直接获取代码的同学,请关注微信公众号「哈喽沃德先生」回复 logistics 即可

  

参考

  

https://mrhelloworld.com/resources/articles/articles_bottom/end02.gif

本文采用 知识共享「署名-非商业性使用-禁止演绎 4.0 国际」许可协议

大家可以通过 分类 查看更多关于 MongoDB 的文章。

  

🤗 您的点赞转发是对我最大的鼓励和支持。

📢 扫码关注 哈喽沃德先生「文档 + 视频」每篇文章都配有专门视频讲解,学习更轻松噢 ~

https://mrhelloworld.com/resources/mrhelloworld/qrcode/OfficialAccounts500-500.gif
「 感谢支持 」