学习淘淘商城第一百零四课(生成订单)

         首先提醒一下,我2017-06-17日上午补充了下上篇博客,没有看完的同学可以回到上篇博客进行学习。这节课我们一起学习下生成订单。

第一部分:订单数据库分析

        我们先来看下tb_order表,如下图所示,可以看到,主键order_id是字符串类型,不是自增长的,因此我们需要自己生成订单编号,我们平时使用京东、天猫等购物网站,发现人家的订单号都是用数字组成的,我们也使用数字作为订单号,但是怎样才能使订单号不重复呢?用时间加随机数的方案生成的订单其实还是可能会重复的,当同一时刻生成的订单越多越有可能出现订单号一样的情况,因此我们不能使用这种方案。比较好的方案是什么呢?是用redis的incr方法,由于redis是单线程的,因此无论多少个线程共同访问也不会出现订单编号一样的情况。payment字段是实付金额,需要从前台传过来,保留小数点后2位,payment_type是支付类型,分为在线支付和货到付款,也需要从前台页面传过来,post_free字段是邮费,邮费得由前台传过来,因为很多电商都搞活动,买够多少钱的东西就免邮费,因此邮费是动态变化的。status字段是订单状态,订单状态我们暂且定义了6种状态,未付款、已付款、未发货、已发货、交易成功、交易关闭。create_time字段是订单创建时间,这没什么可说的,update_time字段是订单更新时间,这个通常是订单状态发生了变化,payment_time字段是付款时间,consign_time字段是发货时间,end_time字段是交易完成时间,这个通常是用户点确认收货的时间,交易关闭时间则是该订单的所有流程都走完后的时间。shipping_name字段是物流名称,即用的谁家的快递。shipping_code字段是物流单号,这个不用废话。user_id字段当然是指购买者ID。buyer_message字段是指买家留言,buyer_nick字段指买家昵称。buyer_rate字段记录买家是否已经评价。表中还可以看到create_time、buyer_nick、status、payment_type四个字段由key修饰,说明为这四个字段建立了索引。


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


         接着我们看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"的用法如下所示

     varStatus属性可以方便我们实现一些与行数相关的功能,如:奇数行、偶数行差异;最后一行特殊处理等等。先就varStatus属性常用参数总结下:
${status.index}      输出行号,从0开始。
${status.count}      输出行号,从1开始。
${status.current}   当前这次迭代的(集合中的)项
${status.first}  判断当前项是否为集合中的第一项,返回值为true或false
${status.last}   判断当前项是否为集合中的最后一项,返回值为true或false
begin、end、step分别表示:起始序号,结束序号,跳跃步伐。

          可以看到我们这里用到的便是其行号功能,而且是从0开始,orderItems是个集合,该集合通过索引号获取它的对象,然后将购物车对象的对应属性赋给orderItems集合中当前索引号下的对象的这个属性,totalPrice是将购物车里每款商品的总价格相加,就是整个订单的总金额。forEach里面的属性值都是tb_order_item表中的字段。forEach之后就是payment字段,该字段也是tb_order表里面的字段,表示付款金额,我们看到了给payment赋的值是value="${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="15891588888"/>
<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="西三旗 xxxxxxxxx"/>
         综合以上情况,我们来写个pojo类包含这些表单信息,那么我们这个pojo应该放到哪儿比较合适呢?我们不能把它放到taotao-common当中,因为我们的taotao-order工程已经依赖了taotao-common工程了,如果taotao-common工程现在再依赖taotao-order,那么便成了相互依赖了,这是断不可行的。我们还想让它尽可能的共用,把它放到taotao-order-interface工程比较合适,因为taotao-order工程及taotao-order-web工程都依赖taotao-order-interface,因此把pojo写到taotao-order-interface工程比较合适。
          pojo类如下图所示,这里用到了一个技巧,那就是继承了TbOrder类,这样OrderInfo便直接拥有了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;
	}
	
	
}
第三部分:生成订单

        首先我们需要在taotao-order-interface工程新建一个接口类并在该接口类中添加一个接口,如下图所示。


       接口类代码如下:

package com.taotao.order.service;

import com.taotao.common.pojo.TaotaoResult;
import com.taotao.order.pojo.OrderInfo;

public interface OrderService {
	//生成订单,OrderInfo当中包含了表单提交的所有数据。
    TaotaoResult createOrder(OrderInfo orderInfo);
}
       下面我们在taotao-order-service工程的com.taotao.order.service.impl包下新建OrderService接口的实现类,如下图所示。

         OrderServiceImpl.java类的所有代码如下:

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.jedis.service.JedisClient;
import com.taotao.mapper.TbOrderItemMapper;
import com.taotao.mapper.TbOrderMapper;
import com.taotao.mapper.TbOrderShippingMapper;
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 tbOrderMapper;
	@Autowired
	private TbOrderItemMapper tbOrderItemMapper;
	@Autowired
	private TbOrderShippingMapper tbOrderShippingMapper;
	@Autowired
	private JedisClient jedisClient;
	
	@Value("${ORDER_ID_GEN_KEY}")
	private String ORDER_ID_GEN_KEY;
	@Value("${ORDER_ID_BEGIN_VALUE}")
	private String ORDER_ID_BEGIN_VALUE;
	@Value("${ORDER_ITEM_ID_GEN_KEY}")
	private String ORDER_ITEM_ID_GEN_KEY;
	

	@Override
	public TaotaoResult createOrder(OrderInfo orderInfo) {
		//生成订单号,可以使用redis的incr方法生成
		if(!jedisClient.exists(ORDER_ID_GEN_KEY)){
			//设置初始值
			jedisClient.set(ORDER_ID_GEN_KEY, ORDER_ID_BEGIN_VALUE);
		}
		String orderId = jedisClient.incr(ORDER_ID_GEN_KEY).toString();
		//需要补全pojo的属性,其它的都是从页面传递过来的
		orderInfo.setOrderId(orderId);
		//付款状态,1、未付款,2、已付款,3、未发货,4、已发货,5、交易成功,6、交易关闭,刚开始肯定是未付款
		orderInfo.setStatus(1);
		//订单创建时间
		orderInfo.setCreateTime(new Date());
		orderInfo.setUpdateTime(new Date());
		//向订单表插入数据,由于OrderInfo继承自TbOrder,因此这里才可以直接把orderInfo作为参数
		tbOrderMapper.insert(orderInfo);
		//向订单明细表插入数据
		List<TbOrderItem> orderItems = orderInfo.getOrderItems();
		for (TbOrderItem tbOrderItem : orderItems) {
			//获得明细主键,第一次使用ORDER_ITEM_ID_GEN_KEY这个key,是没有初始值的,那么会自动将初始值变为1
			String oid = jedisClient.incr(ORDER_ITEM_ID_GEN_KEY).toString();
			//这里之所以只补充了两个属性,是因为tbOrderItem自身已经有itemId了。
			tbOrderItem.setId(oid);//这是订单明细表的主键
			tbOrderItem.setOrderId(orderId);
			//插入明细数据
			tbOrderItemMapper.insert(tbOrderItem);
		}
		//向订单物流表插入数据
		TbOrderShipping orderShipping = orderInfo.getOrderShipping();
		orderShipping.setOrderId(orderId);
		orderShipping.setCreated(new Date());
		orderShipping.setUpdated(new Date());
		tbOrderShippingMapper.insert(orderShipping);
		//返回订单号
		return TaotaoResult.ok(orderId);
	}

}

        既然用到了redis,我们便要添加对redis的依赖、redis的配置文件以及redis的接口和实现类,首先我们在taotao-order-service工程添加对redis的依赖


          依赖代码如下:

<!-- Redis客户端 -->
	<dependency>
		<groupId>redis.clients</groupId>
		<artifactId>jedis</artifactId>
	</dependency>
          下面我们还需要redis的配置文件,我们可以从taotao-manager-service工程的spring目录下复制一份过来,如下图所示,由于初始化Spring容器时以applicationContext-*.xml为条件了,因此凡是以"applicationContext-“”开头的都会被装入容器中。


         applicationContext-jedis.xml文件的全部内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">
    
	
	<!-- redis单机版 -->
	<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
		<constructor-arg name="host" value="192.168.156.21"/>
		<constructor-arg name="port" value="6999"/>
	</bean>
	<bean id="jedisClientPool" class="com.taotao.jedis.service.impl.JedisClientPool"></bean>
	
	 <!-- redis集群版 -->
	<!--<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
		<constructor-arg>
			<set>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.156.15"/>
					<constructor-arg name="port" value="6379"/>
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.156.16"/>
					<constructor-arg name="port" value="6379"/>
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.156.17"/>
					<constructor-arg name="port" value="6379"/>
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.156.18"/>
					<constructor-arg name="port" value="6379"/>
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.156.19"/>
					<constructor-arg name="port" value="6379"/>
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.156.20"/>
					<constructor-arg name="port" value="6379"/>
				</bean>
			</set>
		</constructor-arg>
	</bean>
	<bean id="jedisClientCluster" class="com.taotao.jedis.service.impl.JedisClientCluster"></bean> -->
</beans>
          下面我们从taotao-content-interface工程中复制com.taotao.jedis.service目录到taotao-order-interface工程的src/main/java目录下,然后把taotao-content-service工程的com.taotao.jedisservice.impl包复制到taotao-order-service工程的src/main/java目录下,如下图所示。


          代码中还用到了常量,我们把常量放到配置文件中,如下图所示。


        配置文件的内容如下:

#订单生成key
ORDER_ID_GEN_KEY=ORDER_ID_GEN
#订单号初始值,之所以要有个初始值,是为了避免让消费者认为自己是第一个购买
#这家商品的人,人都不愿意去尝试新的事务,因为有风险,所以给个初始值比较好
ORDER_ID_BEGIN_VALUE=100888
#订单明细表主键生成key
ORDER_ITEM_ID_GEN_KEY=ORDER_ITEM_ID_GEN
        配置文件有了,但是我们要确认Spring加载了该配置文件,我们查看applicationContext-dao.xml文件,发现加载了properties目录下所有以.properties文件结尾的配置文件。所以自然而然resource.properties文件也被加载了。


第四部分:订单生成表现层

        既然服务端写好了,我们便要发布服务,我们在applicationContext-service.xml文件中发布,如下图所示


         发布代码如下:

<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.taotao.order.service.OrderService" ref="orderServiceImpl" timeout="300000"/>
         下面便是在taotao-order-web工程引用dubbo服务,如下图所示。


        引用服务代码如下:

<dubbo:reference interface="com.taotao.order.service.OrderService" id="orderService" />
        接着我们在taotao-order-web工程的OrderController中添加一个接口,如下图所示,其中用到了3天以后的时间,以前我们用Calandar来计算日期,但太麻烦,这里介绍一个简单的方法,那就是使用joda-time-2.5.jar这个包,实例化DateTime,然后直接使用plusDays(3)便可以得到3 天后的日期,是不是非常方便。订单生成成功后,我们要跳转到订单生成成功页面,这个页面是success.jsp,这个页面有三个变量需要从Controller传过来,因此我们便在Controller方法中使用Model带回这三个参数。

 
            Controller代码如下:

@RequestMapping(value="/order/create",method=RequestMethod.POST)
	public String createOrder(OrderInfo orderInfo,Model model){
		//生成订单
		TaotaoResult result = orderSerive.createOrder(orderInfo);
		//返回逻辑视图
		model.addAttribute("orderId", result.getData().toString());
		model.addAttribute("payment", orderInfo.getPayment());
		//得到3天后的日期
		DateTime dateTime = new DateTime();
		dateTime = dateTime.plusDays(3);
		model.addAttribute("date", dateTime.toString("yyyy-MM-dd"));
		//返回逻辑视图
		return "success";
	}
          @RequestMapping("/order/create")这个请求来自于taotao-order-web工程的order-cart.jsp中的隐藏表单,如下图所示。


第五部分:测试

        代码写完了,下面我们来测试,我们先将taotao-order工程打包到本地maven仓库,然后用tomcat插件启动taotao-order工程(这个我已经写过太多遍了,就不再写了),然后再重启taotao-order-web工程。

         今天看了下工程没法启动的原因,原来是由于发布服务的时候,把ref="orderServiceImpl"写成了ref="orderService",导致taotao-order工程启动老是报找不到orderService的错误,细节决定成败,大家写代码的时候一定要细心了。


         我们到订单确认页面,点击"提交订单"。



            我们会看到如下图所示页面,提示订单提交成功了。


             我们再来看看数据库中的数据,可以看到三张表都有数据了。说明我们的订单功能没问题了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值