淘淘商城第113讲——生成订单

本讲我们将一起学习下如何生成订单。

数据库表设计分析

订单表

我们先来看下tb_order表的结构,如下图所示。
在这里插入图片描述
可以看到:
在这里插入图片描述
从订单表的结构中还可以看到create_time、buyer_nick、status、payment_type这四个字段由key修饰,说明咱们为这四个字段建立了索引。

订单项表

我们从订单表中可以看到订单表中并没有购买商品的详情信息,那么商品详情信息在哪儿存放呢?它被存放到了tb_order_item表中,如下图所示,
在这里插入图片描述
可以看到,主键id字段也是个字符串,我们也需要为其生成主键,不过我倒是觉得,如果id是Long类型并且主键自增长那样会更好点。

不知你有没有发现订单项表中的item_id、title、price以及pic_path这4个字段在商品表中都存在,那为什么在订单项表中还要设计这4个字段呢?这就要说说订单项表的设计要求了,即字段尽可能冗余以展示商品的数据,其目的是减少关联查询,提升查询性能。

订单物流表

接着我们再来看看tb_order_shipping表,这张表存放的是用户的收货信息,包括收货人姓名、固定电话、移动电话、省、市、区/县、街道门牌号、邮政编码等,而且收货人信息与订单是一对一的关系,因此订单物流表的主键是order_id。
在这里插入图片描述

小结

这三张表之间的关系是:一个订单对应多个订单项,一个订单对应一个订单物流(运输一个订
单)。
在这里插入图片描述

订单生成页面分析

生成订单是在订单确认页面中进行的,如下图所示,可以看到一个提交订单按钮。
在这里插入图片描述
我们找到这个页面所对应的jsp文件,即order-cart.jsp,搜索提交订单字样,可以看到如下图所示的搜索结果。
在这里插入图片描述
可以看到这是一个button按钮,在该按钮的onclick事件中使用id选择器来得到表单,并且将该表单进行了提交。

那么要被提交的表单在哪儿呢?我们搜索orderForm字样,如下图所示,可以看到这个表单里面所有的标签都是隐藏的,是不会被用户看到的,用户看到的只是表单下面展示的信息(这些信息只是做展示用,不会被提交,真正提交的是被隐藏的表单)。表单要提交的话,我们一般用pojo来接收会比较合适,那么对于这个表单来说,我们应该用什么样的pojo来接收呢?
在这里插入图片描述
我们分析下上图中的表单,这个表单包含了三张表的信息,其中<input type="hidden" name="paymentType" value="1"/>标签对应的便是tb_order表中的付款类型字段,这里默认是1,<c:forEach>标签遍历的是购物车中的商品列表,var="cart"表示购物车中遍历出来的商品对象,varStatus="status"的用法如下所示:
在这里插入图片描述
从上图所示的表单中可以看到我们这里用到的便是其行号功能,而且是从0开始。orderItems是一个集合,该集合通过索引号来获取其中的对象,然后将购物车中商品对象的对应属性赋给orderItems集合中当前索引号下的对象的这个属性(有点绕口);totalPrice是将购物车里每款商品的总价格相加,最终得到的就是整个订单的总金额。

而且,<c:forEach>标签里面的属性值都是tb_order_item表中的字段,<c:forEach>标签之后便是payment字段,该字段也是tb_order表里面的字段,表示付款金额,我们可以看到给payment赋的值是${totalPrice/100 },这里之所以要除100是由于我们的tb_order_item表中定义的商品单价是整数,这个整数是以分为单位乘以100的,这样两位小数的金额比如11.11元便在数据库表中存成了1111,理所应当地,数据库表中存储的金额单位便是分,不再是元了。虽然数据库表中存储的是以分为单位的价格,但是我们展示在页面中的价格一定是要以元为单位的,因此我们就需要写成${totalPrice/100 }了,因为这才是以元为单位的金额。

被隐藏表单中的下面这几句代码意思比较明显,一目了然就知道存放的是收货人地址信息,用到的类是逆向工程生成的TbOrderShipping类。

<input type="hidden" name="orderShipping.receiverName" value="马云"/>
<input type="hidden" name="orderShipping.receiverMobile" value="15895888888"/>
<input type="hidden" name="orderShipping.receiverState" value="浙江"/>
<input type="hidden" name="orderShipping.receiverCity" value="杭州"/>
<input type="hidden" name="orderShipping.receiverDistrict" value="滨江区"/>
<input type="hidden" name="orderShipping.receiverAddress" value="网商路699号"/>

综合以上情况,我们就可以写出一个pojo类来包含以上这些表单信息,那么问题来了,这个pojo应该放到哪儿比较合适呢?我们一定不能把它放到taotao-common工程当中,因为我们的taotao-manager-pojo工程已经依赖taotao-common工程了,如果taotao-common工程现在再依赖taotao-manager-pojo工程,那么便成了相互依赖了,这是断不可行的。但是我们还想让它尽可能的共用,那咋个办呢?由于taotao-order和taotao-order-web这两个工程都依赖了taotao-order-interface工程,因此把pojo放到taotao-order-interface工程中比较合适。

该pojo的代码如下图所示,这里用到了一个技巧,那就是继承了TbOrder类,这样该pojo便直接拥有了TbOrder类的属性。而且为了让该pojo在网络中进行传输,我们还需要让它实现序列化接口。
在这里插入图片描述
为了方便大家复制,现将该pojo的代码贴出,如下所示。

package com.taotao.order.pojo;

import java.io.Serializable;
import java.util.List;

import com.taotao.pojo.TbOrder;
import com.taotao.pojo.TbOrderItem;
import com.taotao.pojo.TbOrderShipping;

public class OrderInfo extends TbOrder implements Serializable {
	
	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;
	}
	
}

生成订单

dao层

由于生成订单只涉及到单表操作,因此我们使用逆向工程生成的dao层代码即可。

service层

由于我们在向tb_order中插入记录时,主键(即订单号)是使用redis的incr命令来帮我们生成的,因此在taotao-order-service工程的pom文件中还要添加对redis的依赖。
在这里插入图片描述
然后将taotao-manager-service工程下的com.taotao.manager.jedis包整个拷贝到taotao-order-service工程的src/main/java目录下,并且重命名为com.taotao.order.jedis,如下图所示。
在这里插入图片描述
接着再将taotao-manager-service工程下的applicationContext-redis.xml配置文件拷贝过来,并对其做一点小小的改动,其实就是改个包名,如下图示所示。
在这里插入图片描述
以上准备工作做好之后,我们就要开始正式编写service层的代码了。

首先我们需要在taotao-order-interface工程中新建一个接口并在该接口中声明一个方法,如下图所示。
在这里插入图片描述
然后在taotao-order-service工程的com.taotao.order.service.impl包下新建一个以上接口的实现类,例如OrderServiceImpl。

package com.taotao.order.service.impl;

import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.taotao.common.pojo.TaotaoResult;
import com.taotao.mapper.TbOrderItemMapper;
import com.taotao.mapper.TbOrderMapper;
import com.taotao.mapper.TbOrderShippingMapper;
import com.taotao.order.jedis.JedisClient;
import com.taotao.order.pojo.OrderInfo;
import com.taotao.order.service.OrderService;
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 orderShippingMapper;
	
	@Autowired
	private JedisClient client;
	
	@Value("${GEN_ORDER_ID_KEY}")
	private String GEN_ORDER_ID_KEY; // 生成订单id的key

	@Value("${GEN_ORDER_ID_INIT}")
	private String GEN_ORDER_ID_INIT; // 订单id初始化的值
	
	@Value("${GEN_ORDER_ITEM_ID_KEY}")
	private String GEN_ORDER_ITEM_ID_KEY; // 设置订单项生成主键的id的key
	
	@Override
	public TaotaoResult createOrder(OrderInfo info) {
		// 1. 插入订单表
		// 通过redis的incr命令生成订单id
		// 判断如果key没有存在,那么我们就需要初始化一个key并设置一个初始值
		if (!client.exists(GEN_ORDER_ID_KEY)) {
			client.set(GEN_ORDER_ID_KEY, GEN_ORDER_ID_INIT);
		}
		String orderId = client.incr(GEN_ORDER_ID_KEY).toString();
		// 再补全其他的属性
		info.setOrderId(orderId);
		info.setPostFee("0");
		info.setStatus(1);
		info.setCreateTime(new Date());
		info.setUpdateTime(info.getCreateTime());
//		info.setUserId(userId); 这个由Controller来设置
//		info.setBuyerNick(buyerNick); 这个由Controller来设置
		// 注入Mapper
		orderMapper.insert(info);
		
		// 2. 插入订单明细表
		List<TbOrderItem> orderItems = info.getOrderItems();
		for (TbOrderItem tbOrderItem : orderItems) {
			// 设置订单明细表的id,也是通过redis的incr命令生成订单明细表的id
			String orderItemId = client.incr(GEN_ORDER_ITEM_ID_KEY).toString();
			// 再补全其他的属性
			tbOrderItem.setId(orderItemId);
			tbOrderItem.setOrderId(orderId);
			// 插入订单项表
			orderItemMapper.insert(tbOrderItem);
		}
		
		// 3. 插入订单物流表
		// 设置订单id
		TbOrderShipping orderShipping = info.getOrderShipping();
		// 再补全其他的属性
		orderShipping.setOrderId(orderId);
		orderShipping.setCreated(info.getCreateTime());
		orderShipping.setUpdated(info.getCreateTime());
		// 插入订单物流表
		orderShippingMapper.insert(orderShipping);
		// 返回一个TaotaoResult对象,并且还要包含订单id
		return TaotaoResult.ok(orderId);
	}

}

注意:在向tb_order中插入记录时,主键(即订单号)是使用redis的incr命令来帮我们生成的,而且该订单号还应该有一个初始值,GEN_ORDER_ID_KEY常量就是对应订单号生成的key。假如说这个key在redis数据库中不存在的话会有什么后果呢?如果原来这个key没有,你直接调用incr这个命令那么就会创建出这个key,而且该key所对应的value是从1开始的,咱们刚才说了这个订单号最好应该有一个初始值,要是太小的话,别人一看一共就没卖出几单去,可能他就没有购买的欲望了,所以给该key一个初始值比较好。

从OrderServiceImpl实现类的代码中我们可以看到还用到了3个常量,通常来说我们应该把这些常量放到配置文件中,如下图所示。
在这里插入图片描述
以上那些常量在配置文件定义好了之后,我们还要确保Spring容器加载了该配置文件。于是,我们去查看一下applicationContext-dao.xml文件,发现确实加载了properties目录下所有以.properties结尾的配置文件。
在这里插入图片描述
最后千万还要记得对外发布Dubbo服务,即在applicationContext-service.xml文件中添加一行如下配置。

<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.taotao.order.service.OrderService" ref="orderServiceImpl" timeout="300000" />

截图如下:
在这里插入图片描述
至此,service层的代码我们就算是编写完了。

表现层

首先在taotao-order-web工程中引用Dubbo服务,即在springmvc.xml文件中添加一行如下配置。

<dubbo:reference interface="com.taotao.order.service.OrderService" id="OrderService" />

截图如下:
在这里插入图片描述
然后在taotao-order-web工程的OrderController类中添加一个方法,如下图所示,其中我们要计算3天以后的时间,以前我们是用Calandar类来计算日期,但是这样做太麻烦了,这里介绍一个简单的方法,那就是使用joda-time-2.5.jar这个jar包,它就是一个时间操作组件。要想计算出3天以后的时间,只须实例化一个DateTime对象,然后直接调用plusDays(3)方法即可,是不是非常方便啊!而且订单生成成功之后,我们还要跳转到订单生成成功页面,这个页面是success.jsp,这个页面有三个变量需要从Controller传递过来,因此我们便在Controller的方法中使用request域带回这三个参数。
在这里插入图片描述
为了方便大家复制,现将OrderController类中的createOrder方法的代码贴出,如下所示。

/**
  * url:/order/create
  * 参数:表单_使用OrderInfo对象来接收
  * 返回值:逻辑视图
  */
@RequestMapping(value="/order/create", method=RequestMethod.POST)
public String createOrder(HttpServletRequest request, OrderInfo info) {
	// 1. 引入服务
	// 2. 注入服务
	// 3. 调用服务
	// 查询用户的信息并设置到info对象中
	TbUser user = (TbUser) request.getAttribute("USER_INFO");
	info.setUserId(user.getId());
	info.setBuyerNick(user.getUsername());
	TaotaoResult result = orderService.createOrder(info);
	request.setAttribute("orderId", result.getData());
	request.setAttribute("payment", info.getPayment());
	// 预计送达时间一般往后拖三天,即订单创建时间往后拖三天
	DateTime dateTime = new DateTime(); // 当前时间
	DateTime plusDays = dateTime.plusDays(3); // 在当前时间上加三天
	request.setAttribute("date", plusDays.toString("yyyy-MM-dd"));
	return "success";
}

@RequestMapping("/order/create")注解中的请求来自于taotao-order-web工程的order-cart.jsp页面中的隐藏表单,如下图所示。
在这里插入图片描述
至此,表现层的代码我们算是编写完了。

测试

代码终于写完了,下面我们便来测试。我们先将taotao-order工程安装到本地maven仓库中,然后用tomcat插件启动taotao-order工程,接着再重启taotao-order-web工程,紧接着进入到订单确认页面中,如下图所示。
在这里插入图片描述
当我们点击上图中的提交订单按钮之后,便会生成一个订单,如下图所示。
在这里插入图片描述
并且数据库中tb_order、tb_order_item以及tb_order_shipping这三张表中已经插入数据了,这儿就不截图了,读者如若不信,可亲测。

至此,订单生成功能圆满完成!!!

评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李阿昀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值