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层的编码如下:

    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。

    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层实现:

    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层实现:

    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层实现:

    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文件中配置上:

  • 相关阅读:
    sublime text 4 vim 插件配置
    ssh-keygen 的使用
    distribution transaction solution
    bilibili 大数据 视频下载 you-get
    Deepin 20.2.1 安装 MS SQL 2019 容器版本
    【转】使用Linux下Docker部署MSSQL并加载主机目录下的数据库
    【转】You Can Now Use OneDrive in Linux Natively Thanks to Insync
    dotnet 诊断工具安装命令
    Linux 使用 xrandr 设置屏幕分辨率
    【转】CentOS 7.9 2009 ISO 官方原版镜像下载
  • 原文地址:https://www.cnblogs.com/fengli9998/p/6417117.html
Copyright © 2011-2022 走看看