zoukankan      html  css  js  c++  java
  • 订单功能模块

    订单功能模块设计与实现

    在商城项目中,之前我们介绍了购物车功能模块的实现,商品加入到购物车之后,就是到购物车结算,然后显示购物车的商品列表,点击去结算,然后到了未提交前的订单列表,

    点击提交订单后,生成此订单,返回订单的订单号,付款金额,订单预计到达时间。订单系统是一个非常重要的系统,我们的移动端、PC端都需要订单系统,所以这里我们将订单系统单独作为一个服务来,留出接口供客户单来调用。

    今天我们来看下这个订单系统到底是如何实现的:

    一、订单系统功能

    订单系统主要包含哪些功能模块呢?

    创建订单功能、查看订单列表、根据订单id查询订单的详细信息、订单修改、订单取消、订单状态、订单评价等功能的实现。

    今天我们来看下创建订单的流程:

    二、订单系统的数据库表的设计

    创建订单说到底就是向订单表中添加数据,即insert这些信息。

    下单功能一定要使用关系型数据库表,保证数据的一致性,因为创建订单要保证在一个事务(一个事务就是指向数据库中进行的一种操作:比如插入,删除等等)里面,nosql数据库不支持事务,可能会丢失数据。

    我们在网上购物的时候通常这个订单包含的信息比较多,所以对于订单系统如何创建它的数据库也是非常重要的。创建数据库遵循数据库设计的三大范式原则来设计。

    我们创建了三个表:tb_order(订单信息表),tb_order_item(订单详情表),tb_order_shipping(订单配送表).

    tb_order:这里包含了订单的基本信息

      

    tb_order_item:订单详情表:订单的详情主要就是购买商品的信息,通过订单的id来实现关联

    tb_order_shipping:订单配送表:

    这是三个基本的表,其实还可以有物流信息表,订单交易信息表。这里我们采用这三张表足够。

    三、订单系统接口文档,一般我们开发的时候会收到已经写好的接口文档,比如创建订单的接口文档。

    从上面这个表中,我们可以看到该接口的url,接口的传入参数和返回值。

    接下来我们针对这三个来进行代码的编写:

    url属于controller层,

    传入参数这里我们可以看到是数据库建立的三张表信息:第一个是tb_order,第二个是一个集合式的订单明细List,第三个是订单的配送信息表。

    所以传入参数就是这三个对象。这里我们是编写接口,供客户端调用,至于客户端怎么将这些参数传递过来,那是客户端团队考虑的事情。

    返回值这里使用了taotaoresult来包装了下,因为我们提交订单成功后,返回的是订单号,即订单的id所以,我们需要向客户端传递订单id过去,并显示在订单创建成功的页面。

    下面看下订单服务接口的service层的实现:

    service层的主要实现是将订单信息添加到数据库中,即接收controller传递过来的对象,然后补全页面没有的字段,insert数据库,这里可以使用逆向工程生成的dao。

    另外还有个问题:

    订单编号:订单编号用什么形式比较好呢?

    解决方案一(不能使用):

    使用mysql的自增长。

    优点:不需要我们自己生成订单号,mysql会自动生成。

    缺点:如果订单表数量太大时需要分库分表,此时订单号会重复。如果数据备份后再恢复,订单号会变。

    方案二:日期+随机数

    采用毫秒+随机数。

    缺点:仍然有重复的可能。不建议采用此方案。在没有更好的解决方案之前可以使用。

    方案三:使用UUID 

    优点:不会重复。

    缺点:长。可读性查。不建议使用。 

    方案四:可读性好,不能太长。一般订单都是全数字的。可以使用redis的incr命令生成订单号。

    优点:可读性好,不会重复

    缺点:需要搭建redis服务器。

    所以我们选取方案四作为生成订单号的方案。

    那么service层的编码如下:

    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
    package com.taotao.order.service.impl;
     
    import java.util.Date;
    import java.util.List;
     
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Service;
     
    import com.taotao.common.utils.TaotaoResult;
    import com.taotao.mapper.TbOrderItemMapper;
    import com.taotao.mapper.TbOrderMapper;
    import com.taotao.mapper.TbOrderShippingMapper;
    import com.taotao.order.dao.JedisClient;
    import com.taotao.order.service.OrderService;
    import com.taotao.pojo.TbOrder;
    import com.taotao.pojo.TbOrderItem;
    import com.taotao.pojo.TbOrderShipping;
    //订单模块实现
    @Service
    public class OrderServiceImpl implements OrderService {
        @Autowired
        private TbOrderMapper orderMapper;
        @Autowired
        private TbOrderItemMapper orderItemMapper;
        @Autowired
        private TbOrderShippingMapper shippingMapper;
        @Autowired
        private JedisClient jedisClient;
        @Value("${ORDER_GEN_KEY}")
        private String ORDER_GEN_KEY;
        @Value("${ORDER_DEFAULT_KEY}")
        private String ORDER_DEFAULT_KEY;
        @Value("${ORDER_ITEM_KEY}")
        private String ORDER_ITEM_KEY;
         
    //创建订单功能实现
        @Override
        public TaotaoResult createOrder(TbOrder order,List<TbOrderItem> orderItem, TbOrderShipping orderShipping) {
            //逻辑步骤:创建订单即向数据库中三个表中插入数据,所以讲数据库中的字段补全即可。页面传递过来的就不需要补全了,直接可以写入数据库
            /*第一步:订单号的生成方式:因为订单号的特殊性,订单后最好采用数字组成,且不能重复,可以采用redis中自增长的方式来实现,
             * 在redis中,如果无key,则redis自动创建你写的key的名字。采用incr的命令来实现订单号的自增。默认自增是从1开始的,因为考虑到用户的原因,在redis中设置一个默认的key值
             * 这样用户体验会好些,不会因为看到简单的1,2,3,所以设置一个默认的key值
            */
            String string = jedisClient.get(ORDER_GEN_KEY);
            if (StringUtils.isBlank(string)) {
                //如果redis中的key为空,则利用默认值
                String defaultKey = jedisClient.set(ORDER_GEN_KEY,ORDER_DEFAULT_KEY);  
            }
            //如果存在此key则,采用incr命令自增
            long orderId= jedisClient.incr(ORDER_GEN_KEY);
            //然后向订单表中插入数据
            order.setOrderId(orderId+"");
            //订单状态:状态:1、未付款,2、已付款,3、未发货,4、已发货
            order.setStatus(1);
            order.setCreateTime(new Date());
            order.setUpdateTime(new Date());
            order.setBuyerRate(0);
            //补全完信息后,插入数据库表
            orderMapper.insert(order);
            //补全完订单表后,将订单明细表补全,因为之前订单写入redis,订单明细的id也写入redis吧,自动创建这个id的key。这个不需要默认编号了。对比页面传递的参数,将剩下的补全
           String string2 = jedisClient.get(ORDER_ITEM_KEY);
           long orderItemId = jedisClient.incr(string2);
           //因为传递过来的是一个集合列表,所以遍历列表
           for (TbOrderItem tbOrderItem : orderItem) {
               tbOrderItem.setId(orderItemId+"");
               tbOrderItem.setOrderId(orderId+""); 
               orderItemMapper.insert( tbOrderItem);
        
            //接下来补全订单配送表
            orderShipping.setOrderId(orderId+"");
            orderShipping.setCreated(new Date());
            orderShipping.setUpdated(new Date());
            shippingMapper.insert(orderShipping);
             
            return TaotaoResult.ok(orderId);
        }
     
    }

     controller:层实现

    controller需要将对象传递给service层:(客户端向服务器端传入的参数格式,详见后面博文)

    我们看到接口文档中,controller接收的参数是一个json格式的字符串,也就是说客户端传递过来的是json格式的字符串。

    这就涉及到springMVC是如何接收json字符串的,需要用到@RequestBody注解。这里多说几句:

    @ResponseBody注解的原理是response只能响应一个字符串,当我们的返回值是java对象的时候,它有一个默认行为,即利用jackson包将java对象转为字符串响应。这是一个默认自动的行为,不需要我们设置,只要这个注解即可。

    @RequestBody注解同理:利用这个注解告诉springMVC我们现在接收的是一个json字符串,需要采取默认行为利用jackson包将json字符串转换为java对象,所以controller层我们需要一个java对象的pojo。

    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
    package com.taotao.order.pojo;
     
    import java.util.List;
     
    import com.taotao.pojo.TbOrder;
    import com.taotao.pojo.TbOrderItem;
    import com.taotao.pojo.TbOrderShipping;
     
    public class Order extends TbOrder {
        private List<TbOrderItem> orderItems;
        private TbOrderShipping orderShipping;
        public List<TbOrderItem> getOrderItems() {
            return orderItems;
        }
        public void setOrderItems(List<TbOrderItem> orderItems) {
            this.orderItems = orderItems;
        }
        public TbOrderShipping getOrderShipping() {
            return orderShipping;
        }
        public void setOrderShipping(TbOrderShipping orderShipping) {
            this.orderShipping = orderShipping;
        }
     
    }

      controller层实现:

    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.taotao.order.controller;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
     
    import com.taotao.common.utils.ExceptionUtil;
    import com.taotao.common.utils.TaotaoResult;
    import com.taotao.order.pojo.Order;
    import com.taotao.order.service.OrderService;
    //订单管理模块
    @Controller
    @RequestMapping("/order")
    public class OrderController {
        @Autowired
        private OrderService orderService;
        @RequestMapping("/create")
        @ResponseBody
        public TaotaoResult createOrder(@RequestBody Order order ){//创建订单模块实现
            try {
                TaotaoResult taotaoResult = orderService.createOrder(order ,order.getOrderItems(), order.getOrderShipping());
                return taotaoResult;
            catch (Exception e) {
                e.printStackTrace();
                return TaotaoResult.build(500,ExceptionUtil.getStackTrace(e));
            }
         
        }
     
    }

      以上代码是订单服务接口的创建订单接口代码实现。

    接下来我们看下客户端如何调用订单服务层的:

    客户端当我们点击去购物车结算的时候,显示购物车列表。

    在购物车列表页面,当点击去结算的时候,跳转到未提交订单的页面。

    点击提交订单,跳转到显示成功页面。

    controller层实现:

    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
    package com.taotao.portal.controller;
     
    import java.util.List;
     
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
     
    import org.joda.time.DateTime;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
     
    import com.taotao.portal.pojo.CartItem;
    import com.taotao.portal.pojo.Order;
    import com.taotao.portal.service.CartService;
    import com.taotao.portal.service.OrderService;
     
    @Controller
    @RequestMapping("/order")
    public class OrderController {
        @Autowired
        private CartService cartService;
        @Autowired
        private OrderService orderService;
        @RequestMapping("/order-cart")
        //点击去结算,显示订单的页面
        public String showOrderPage(HttpServletRequest request,HttpServletResponse response,Model model){
            List<CartItem> list= cartService.showCartList(request);
            model.addAttribute("cartList", list);
            return "order-cart";   
        }
        //点击提交订单,显示订单号,订单金额,预计送达时间
        @RequestMapping("/create")
        public String showSuccessOrder(Order order,Model model){
         try {
     
            String orderId= orderService.createOrder(order);
            model.addAttribute("orderId ",orderId);
            model.addAttribute("payment",order.getPayment());
            model.addAttribute("date"new DateTime().plusDays(3).toString("yyyy-mm-dd"));
            return "success";
         
        catch (Exception e) {
        e.printStackTrace();
        model.addAttribute("message""创建订单出错");
        return "error/exception";
       }
        }
    }

      service层实现:

    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.taotao.portal.service.impl;
     
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Service;
     
    import com.taotao.common.utils.HttpClientUtil;
    import com.taotao.common.utils.JsonUtils;
    import com.taotao.common.utils.TaotaoResult;
    import com.taotao.portal.pojo.Order;
    import com.taotao.portal.service.OrderService;
    @Service
    public class OrderServiceImpl implements OrderService {
        @Value("${ORDER_BASE_URL}")
        private String ORDER_BASE_URL;
        @Value("${ORDER_CREATE_URL}")
        private String ORDER_CREATE_URL;
         
    //创建订单的客户端实现
        @Override
        public String createOrder(Order order) {
        //因为httpclientUtil中dopost传递的是json格式的数据,所以需要将order这个java对象转换成Json格式 
            String json = HttpClientUtil.doPost(ORDER_BASE_URL+ORDER_CREATE_URL+JsonUtils.objectToJson(order));
            //为了获取里面的订单id,我们需要将获得的字符串转换为taotaoresult,然后获取数据,因为服务层传递过来的就是订单的id,所以获得数据也是获得的id
            TaotaoResult result= TaotaoResult.format(json);
            if (result.getStatus()==200) {
                Object orderId = result.getData();
                return orderId.toString();
            }
            return "";
        }
     
    }

      这里同样用了pojo类Order类。

    拦截器的问题:因为提交订单我们需要用户登录,所以在springMVC.xml文件中配置上:

  • 相关阅读:
    windows下mysql初始密码设置
    python生成简单的验证码
    python中HTMLParser简单理解
    Windows批处理(cmd/bat)常用命令小结
    文件结束的判断和结束符的理解
    交换机与路由器
    结构体字节对齐
    有(无)符号char型及其溢出问题
    kubernetes离线包安装教程
    kubernetes(K8S)快速安装与配置集群搭建图文教程
  • 原文地址:https://www.cnblogs.com/cgfpx/p/11664089.html
Copyright © 2011-2022 走看看