本文我们将一起学习下如何生成订单。
表中还可以看到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来接收呢?
我们分析下上图的表单,这个表单中包含了三张表的信息,其中便是tb_order表中的付款类型字段,这里默认是1了,
标签遍历的是购物车列表,var="cart"
表示单个购物车对象,varStatus="status"
的用法如下所示:
varStatus属性可以方便我们实现一些与行数相关的功能,如:奇数行、偶数行差异;最后一行特殊处理等等。先就varStatus属性常用参数总结下:
可以看到我们这里用到的便是其行号功能,而且是从0开始,orderItems是个集合,该集合通过索引号获取它的对象,然后将购物车对象的对应属性赋给orderItems集合中当前索引号下的对象的这个属性,totalPrice是将购物车里每款商品的总价格相加,就是整个订单的总金额。
标签里面的属性值都是tb_order_item表中的字段。
标签之后就是payment字段,该字段也是tb_order表里面的字段,表示付款金额,我们看到了给payment赋的值是value=”${totalPrice/100 }”,这里之所以要除100是由于我们的tb_order_item表中定义的商品单价便是整数,这个整数是以分为单位乘以100的,这样两位小数的金额比如11.11元便在数据库表中存成了1111,当然了,数据库表中存储的金额单位便是分了,不再是元了。数据库表中虽然存储的是以分为单位的价格,但是我们展示在页面的价格肯定是以元为单位的,因此我们需要让totalPrice/100,这才是以元为单位的金额。接着,下面这几句代码意思比较明显,显然存放的是收货人地址信息,用到的类是逆向生成的TbOrderShipping类。
type="hidden" name="orderShipping.receiverName" value="入云龙"/>
type="hidden" name="orderShipping.receiverMobile" value="15891588888"/>
type="hidden" name="orderShipping.receiverState" value="北京"/>
type="hidden" name="orderShipping.receiverCity" value="北京"/>
type="hidden" name="orderShipping.receiverDistrict" value="昌平区"/>
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在网络中传输,我们需要让它实现序列化接口。
为了方便大家复制,现将OrderInfo类的代码给出。
public class OrderInfo extends TbOrder implements Serializable {
// 商品列表
private List orderItems;
// 收货地址
private TbOrderShipping orderShipping;
public List getOrderItems() {
return orderItems;
}
public void setOrderItems(List orderItems) {
this.orderItems = orderItems;
}
public TbOrderShipping getOrderShipping() {
return orderShipping;
}
public void setOrderShipping(TbOrderShipping orderShipping) {
this.orderShipping = orderShipping;
}
}
首先我们需要在taotao-order-interface工程中新建一个接口并在该接口中添加一个方法,如下图所示。
紧接着我们在taotao-order-service工程的com.taotao.order.service.impl包下新建OrderService接口的实现类,如下图所示。
为方便大家复制,现将OrderServiceImpl实现类的代码给出。
/**
* 订单处理Service
* Title: OrderServiceImpl
* Description:
* Company: www.itcast.cn
* @version 1.0
*/
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private TbOrderMapper orderMapper;
@Autowired
private TbOrderItemMapper orderItemMapper;
@Autowired
private TbOrderShippingMapper orderShippingMapper;
@Autowired
private JedisClient jedisClient;
@Value("${ORDER_GEN_KEY}")
private String ORDER_GEN_KEY;
@Value("${ORDER_ID_INIT}")
private String ORDER_ID_INIT;
@Value("${ORDER_DETAIL_GEN_KEY}")
private String ORDER_DETAIL_GEN_KEY;
@Override
public TaotaoResult createOrder(OrderInfo orderInfo) {
// 生成一个订单号,使用Redis的incr命令来生成
// ORDER_GEN_KEY就是对应订单号生成的key
// 判断订单号生成的key是否存在
if (!jedisClient.exists(ORDER_GEN_KEY)) {
// 如果不存在,要设置初始值
jedisClient.set(ORDER_GEN_KEY, ORDER_ID_INIT);
}
String orderId = jedisClient.incr(ORDER_GEN_KEY).toString();
// 向订单表插入数据
orderInfo.setOrderId(orderId);
// 免邮费
orderInfo.setPostFee("0");
// 订单状态
// 状态:1、未付款,2、已付款,3、未发货,4、已发货,5、交易成功,6、交易关闭
orderInfo.setStatus(1);
Date date = new Date();
orderInfo.setCreateTime(date);
orderInfo.setUpdateTime(date);
// 插入数据
orderMapper.insert(orderInfo);
// 向订单明细表插入数据
List orderItems = orderInfo.getOrderItems();
for (TbOrderItem tbOrderItem : orderItems) {
// 生成一个订单明细表的主键
Long orderDetailId = jedisClient.incr(ORDER_DETAIL_GEN_KEY);
tbOrderItem.setId(orderDetailId.toString());
// 设置订单id
tbOrderItem.setOrderId(orderId);
// 插入数据
orderItemMapper.insert(tbOrderItem);
}
// 向订单物流信息表插入数据
TbOrderShipping orderShipping = orderInfo.getOrderShipping();
orderShipping.setOrderId(orderId);
orderShipping.setCreated(date);
orderShipping.setUpdated(date);
orderShippingMapper.insert(orderShipping);
// 返回订单号
return TaotaoResult.ok(orderId);
}
}
注意:向tb_order中插入记录时,我们是使用Redis的incr命令生成的订单号,而且该订单号还应有一个初始值。ORDER_GEN_KEY常量就是对应订单号生成的key,假如说这个key在Redis中不存在的话会有什么后果?如果原来这个key没有,你直接调用incr这个命令那么就会创建这个key,它是从1开始的序列,咱们刚才说了这个订单号最好应该有一个初始值,要不太小的话,别人一看一共就没卖出几单去,别人就没有购买的欲望了,所以给一个初始值比较好。
OrderServiceImpl实现类的代码中用到了常量,我们把常量放到配置文件中,如下图所示。
配置文件有了,但是我们要确认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服务,如下图所示。
引用服务的代码如下:
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带回这三个参数。
为方便大家复制,现将OrderController类中createOrder方法的代码给出。
@RequestMapping(value="/order/create", method=RequestMethod.POST)
public String createOrder(OrderInfo orderInfo, Model model, HttpServletRequest request) {
// 取用户id
TbUser user = (TbUser) request.getAttribute("user");
orderInfo.setUserId(user.getId());
orderInfo.setBuyerNick(user.getUsername());
// 调用服务生成订单
TaotaoResult result = orderService.createOrder(orderInfo);
// 取订单号并传递给页面
String orderId = result.getData().toString();
model.addAttribute("orderId", orderId);
model.addAttribute("payment", orderInfo.getPayment());
// 预计送达时间是三天后
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工程。
当我们点击下图所示的”提交订单”按钮后。
便会生成一个订单,如下图所示。
并且数据库中tb_order、tb_order_item以及tb_order_shipping这三张表已经插入数据了,这儿就不截图了,读者如若不信,可亲测。
至此,生成订单功能圆满完成!!!